diff --git a/activejdbc/pom.xml b/activejdbc/pom.xml new file mode 100644 index 0000000000..7a49d37411 --- /dev/null +++ b/activejdbc/pom.xml @@ -0,0 +1,129 @@ + + 4.0.0 + com.baeldung + activejdbc + 1.0-SNAPSHOT + jar + activejdbc + http://maven.apache.org + + UTF-8 + 1.4.13 + development.test,development + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + 1.8 + 1.8 + UTF-8 + + + + org.javalite + activejdbc-instrumentation + ${activejdbc.version} + + + process-classes + + instrument + + + + + + org.javalite + db-migrator-maven-plugin + ${activejdbc.version} + + ${project.basedir}/src/main/resources/database.properties + ${environments} + + + + mysql + mysql-connector-java + 5.1.34 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + brief + true + false + + **/*Spec*.java + **/*Test*.java + + + **/helpers/* + **/*$* + + + + + + + + junit + junit + 4.12 + test + + + org.javalite + activejdbc + ${activejdbc.version} + + + opensymphony + oscache + + + + + mysql + mysql-connector-java + 5.1.34 + + + org.slf4j + slf4j-simple + 1.7.9 + + + + + snapshots1 + JavaLite Snapshots1 + http://repo.javalite.io/ + + true + always + warn + + + + + + snapshots2 + JavaLite Snapshots2 + http://repo.javalite.io/ + + true + always + warn + + + + diff --git a/activejdbc/src/main/java/com/baeldung/ActiveJDBCApp.java b/activejdbc/src/main/java/com/baeldung/ActiveJDBCApp.java new file mode 100644 index 0000000000..8906d3e759 --- /dev/null +++ b/activejdbc/src/main/java/com/baeldung/ActiveJDBCApp.java @@ -0,0 +1,61 @@ +package com.baeldung; + + +import com.baeldung.model.Employee; +import com.baeldung.model.Role; +import org.javalite.activejdbc.Base; +import org.javalite.activejdbc.LazyList; +import org.javalite.activejdbc.Model; + +public class ActiveJDBCApp +{ + public static void main( String[] args ) + { + try { + Base.open(); + ActiveJDBCApp app = new ActiveJDBCApp(); + app.create(); + app.update(); + app.delete(); + app.deleteCascade(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + Base.close(); + } + } + + protected void create() { + Employee employee = new Employee("Hugo","C","M","BN"); + employee.saveIt(); + employee.add(new Role("Java Developer","BN")); + LazyList all = Employee.findAll(); + System.out.println(all.size()); + } + + protected void update() { + Employee employee = Employee.findFirst("first_name = ?","Hugo"); + employee.set("last_namea","Choi").saveIt(); + employee = Employee.findFirst("last_name = ?","Choi"); + System.out.println(employee.getString("first_name") + " " + employee.getString("last_name")); + } + + protected void delete() { + Employee employee = Employee.findFirst("first_name = ?","Hugo"); + employee.delete(); + employee = Employee.findFirst("last_name = ?","Choi"); + if(null == employee){ + System.out.println("No such Employee found!"); + } + } + + protected void deleteCascade() { + create(); + Employee employee = Employee.findFirst("first_name = ?","Hugo"); + employee.deleteCascade(); + employee = Employee.findFirst("last_name = ?","C"); + if(null == employee){ + System.out.println("No such Employee found!"); + } + } +} diff --git a/activejdbc/src/main/java/com/baeldung/model/Employee.java b/activejdbc/src/main/java/com/baeldung/model/Employee.java new file mode 100644 index 0000000000..b7fa8aaf1f --- /dev/null +++ b/activejdbc/src/main/java/com/baeldung/model/Employee.java @@ -0,0 +1,19 @@ +package com.baeldung.model; + + +import org.javalite.activejdbc.Model; + +public class Employee extends Model { + + public Employee(){ + + } + + public Employee(String firstName, String lastName, String gender, String createdBy) { + set("first_name1",firstName); + set("last_name",lastName); + set("gender",gender); + set("created_by",createdBy); + } + +} diff --git a/activejdbc/src/main/java/com/baeldung/model/Role.java b/activejdbc/src/main/java/com/baeldung/model/Role.java new file mode 100644 index 0000000000..3f425dbe6b --- /dev/null +++ b/activejdbc/src/main/java/com/baeldung/model/Role.java @@ -0,0 +1,18 @@ +package com.baeldung.model; + + +import org.javalite.activejdbc.Model; +import org.javalite.activejdbc.annotations.Table; + +@Table("EMP_ROLES") +public class Role extends Model { + + public Role(){ + + } + + public Role(String role,String createdBy){ + set("role_name",role); + set("created_by",createdBy); + } +} diff --git a/activejdbc/src/main/migration/_create_tables.sql b/activejdbc/src/main/migration/_create_tables.sql new file mode 100644 index 0000000000..19fc1e72d7 --- /dev/null +++ b/activejdbc/src/main/migration/_create_tables.sql @@ -0,0 +1,25 @@ +# noinspection SqlNoDataSourceInspectionForFile + +create table organisation.employees +( + id int not null auto_increment + primary key, + first_name varchar(100) not null, + last_name varchar(100) not null, + gender varchar(1) not null, + created_at datetime not null, + updated_at datetime null, + created_by varchar(100) not null, + updated_by varchar(100) null +)ENGINE = InnoDB DEFAULT CHARSET = utf8; + +create table organisation.emp_roles +( + id int not null auto_increment primary key, + employee_id int not null, + role_name varchar(100) not null, + created_at datetime not null, + updated_at datetime null, + created_by varchar(100) not null, + updated_by varchar(100) null +)ENGINE = InnoDB DEFAULT CHARSET = utf8; diff --git a/activejdbc/src/main/resources/database.properties b/activejdbc/src/main/resources/database.properties new file mode 100644 index 0000000000..7e665fe8a1 --- /dev/null +++ b/activejdbc/src/main/resources/database.properties @@ -0,0 +1,10 @@ +development.driver=com.mysql.jdbc.Driver +development.username=root +development.password=123456 +development.url=jdbc:mysql://localhost/organisation + + +development.test.driver=com.mysql.jdbc.Driver +development.test.username=root +development.test.password=123456 +development.test.url=jdbc:mysql://localhost/organisation_test \ No newline at end of file diff --git a/activejdbc/src/test/java/com/baeldung/ActiveJDBCAppTest.java b/activejdbc/src/test/java/com/baeldung/ActiveJDBCAppTest.java new file mode 100644 index 0000000000..316dc34712 --- /dev/null +++ b/activejdbc/src/test/java/com/baeldung/ActiveJDBCAppTest.java @@ -0,0 +1,51 @@ +package com.baeldung; + +import com.baeldung.model.Employee; +import com.baeldung.model.Role; +import org.javalite.activejdbc.test.DBSpec; +import org.junit.Test; + +import java.util.List; + +public class ActiveJDBCAppTest extends DBSpec +{ + @Test + public void ifEmployeeCreated_thenIsValid() { + Employee employee = new Employee("B", "N", "M", "BN"); + the(employee).shouldBe("valid"); + } + + @Test + public void ifEmployeeCreatedWithRoles_thenShouldPersist() { + Employee employee = new Employee("B", "N", "M", "BN"); + employee.saveIt(); + employee.add(new Role("Java Developer","BN")); + employee.add(new Role("Lead Java Developer","BN")); + a(Role.count()).shouldBeEqual(2); + List roles = employee.getAll(Role.class).orderBy("created_at"); + the(roles.get(0).getRoleName()).shouldBeEqual("Java Developer"); + the(roles.get(1).getRoleName()).shouldBeEqual("Lead Java Developer"); + } + + @Test + public void ifEmployeeCreatedWithRoles_whenNameUpdated_thenShouldShowNewName() { + Employee employee = new Employee("Binesh", "N", "M", "BN"); + employee.saveIt(); + employee.add(new Role("Java Developer","BN")); + employee.add(new Role("Lead Java Developer","BN")); + employee = Employee.findFirst("first_name = ?", "Binesh"); + employee.set("last_name","Narayanan").saveIt(); + Employee updated = Employee.findFirst("first_name = ?", "Binesh"); + the(updated.getLastName()).shouldBeEqual("Narayanan"); + } + + @Test + public void ifEmployeeCreatedWithRoles_whenDeleted_thenShouldNotBeFound() { + Employee employee = new Employee("Binesh", "N", "M", "BN"); + employee.saveIt(); + employee.add(new Role("Java Developer","BN")); + employee.delete(); + employee = Employee.findFirst("first_name = ?", "Binesh"); + the(employee).shouldBeNull(); + } +} diff --git a/apache-poi/README.md b/apache-poi/README.md index 10fe8ba2e7..c052bc9bf6 100644 --- a/apache-poi/README.md +++ b/apache-poi/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [Microsoft Word Processing in Java with Apache POI](http://www.baeldung.com/java-microsoft-word-with-apache-poi) - [Working with Microsoft Excel in Java](http://www.baeldung.com/java-microsoft-excel) +- [Creating a MS PowerPoint Presentation in Java](https://github.com/eugenp/tutorials/tree/master/apache-poi) diff --git a/core-java-8/README.md b/core-java-8/README.md index 53e8e1a44a..9260e3d447 100644 --- a/core-java-8/README.md +++ b/core-java-8/README.md @@ -37,3 +37,6 @@ - [Iterable to Stream in Java](http://www.baeldung.com/java-iterable-to-stream) - [Converting String to Stream of chars](http://www.baeldung.com/java-string-to-stream) - [How to Iterate Over a Stream With Indices](http://www.baeldung.com/java-stream-indices) +- [Efficient Word Frequency Calculator in Java](http://www.baeldung.com/java-word-frequency) +- [Primitive Type Streams in Java 8](http://www.baeldung.com/java-8-primitive-streams) +- [Fail-Safe Iterator vs Fail-Fast Iterator](http://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator) diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java index b906acfad9..024d5dabdb 100644 --- a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java @@ -1,45 +1,19 @@ package com.baeldung.spliteratorAPI; -import java.util.Arrays; import java.util.List; -import java.util.Spliterator; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; -import java.util.stream.StreamSupport; public class Executor { - public void executeCustomSpliterator() { - Article article = new Article(Arrays.asList(new Author("Ahmad", 0), new Author("Eugen", 0), new Author("Alice", 1), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), - new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), - new Author("Alice", 1), new Author("Mike", 0), new Author("Michał", 0), new Author("Loredana", 1)), 0); - Stream stream = IntStream.range(0, article.getListOfAuthors() - .size()) - .mapToObj(article.getListOfAuthors()::get); - System.out.println("count= " + countAutors(stream.parallel())); - Spliterator spliterator = new RelatedAuthorSpliterator(article.getListOfAuthors()); - Stream stream2 = StreamSupport.stream(spliterator, true); - System.out.println("count= " + countAutors(stream2.parallel())); - } - public void executeSpliterator() { - Spliterator
split1 = generateElements().spliterator(); - Spliterator
split2 = split1.trySplit(); - ExecutorService service = Executors.newCachedThreadPool(); - service.execute(new Task(split1)); - service.execute(new Task(split2)); - } + public static int countAutors(Stream stream) { + RelatedAuthorCounter wordCounter = stream.reduce(new RelatedAuthorCounter(0, true), + RelatedAuthorCounter::accumulate, RelatedAuthorCounter::combine); + return wordCounter.getCounter(); + } - private static int countAutors(Stream stream) { - RelatedAuthorCounter wordCounter = stream.reduce(new RelatedAuthorCounter(0, true), RelatedAuthorCounter::accumulate, RelatedAuthorCounter::combine); - return wordCounter.getCounter(); - } + public static List
generateElements() { + return Stream.generate(() -> new Article("Java")).limit(35000).collect(Collectors.toList()); + } - private List
generateElements() { - return Stream.generate(() -> new Article("Java")) - .limit(35000) - .collect(Collectors.toList()); - } -} +} \ No newline at end of file diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java index ae91c6fe6c..0a7190964e 100644 --- a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java @@ -2,46 +2,48 @@ package com.baeldung.spliteratorAPI; import java.util.List; import java.util.Spliterator; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class RelatedAuthorSpliterator implements Spliterator { - private final List list; - private int current = 0; + private final List list; + AtomicInteger current = new AtomicInteger(); - public RelatedAuthorSpliterator(List list) { - this.list = list; - } + public RelatedAuthorSpliterator(List list) { + this.list = list; + } - @Override - public boolean tryAdvance(Consumer action) { - action.accept(list.get(current++)); - return current < list.size(); - } + @Override + public boolean tryAdvance(Consumer action) { - @Override - public Spliterator trySplit() { - int currentSize = list.size() - current; - if (currentSize < 10) { - return null; - } - for (int splitPos = currentSize / 2 + current; splitPos < list.size(); splitPos++) { - if (list.get(splitPos) - .getRelatedArticleId() == 0) { - Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current, splitPos)); - current = splitPos; - return spliterator; - } - } - return null; - } + action.accept(list.get(current.getAndIncrement())); + return current.get() < list.size(); + } - @Override - public long estimateSize() { - return list.size() - current; - } + @Override + public Spliterator trySplit() { + int currentSize = list.size() - current.get(); + if (currentSize < 10) { + return null; + } + for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) { + if (list.get(splitPos).getRelatedArticleId() == 0) { + Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos)); + current.set(splitPos); + return spliterator; + } + } + return null; + } + + @Override + public long estimateSize() { + return list.size() - current.get(); + } + + @Override + public int characteristics() { + return CONCURRENT; + } - @Override - public int characteristics() { - return SIZED + CONCURRENT; - } } diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java index 108fdc52aa..70435d1c75 100644 --- a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java @@ -1,8 +1,9 @@ package com.baeldung.spliteratorAPI; import java.util.Spliterator; +import java.util.concurrent.Callable; -public class Task implements Runnable { +public class Task implements Callable { private Spliterator
spliterator; private final static String SUFFIX = "- published by Baeldung"; @@ -11,7 +12,7 @@ public class Task implements Runnable { } @Override - public void run() { + public String call() { int current = 0; while (spliterator.tryAdvance(article -> { article.setName(article.getName() @@ -20,7 +21,7 @@ public class Task implements Runnable { current++; } ; - System.out.println(Thread.currentThread() - .getName() + ":" + current); + return Thread.currentThread() + .getName() + ":" + current; } } diff --git a/core-java-8/src/test/java/com/baeldung/spliteratorAPI/ExecutorTest.java b/core-java-8/src/test/java/com/baeldung/spliteratorAPI/ExecutorTest.java new file mode 100644 index 0000000000..64dd65cf5e --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/spliteratorAPI/ExecutorTest.java @@ -0,0 +1,44 @@ +package com.baeldung.spliteratorAPI; + +import java.util.Arrays; +import java.util.Spliterator; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static org.assertj.core.api.Assertions.*; +import org.junit.Before; +import org.junit.Test; + +public class ExecutorTest { + Article article; + Stream stream; + Spliterator spliterator; + Spliterator
split1; + Spliterator
split2; + + @Before + public void init() { + article = new Article(Arrays.asList(new Author("Ahmad", 0), new Author("Eugen", 0), new Author("Alice", 1), + new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), + new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), + new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), + new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), + new Author("Mike", 0), new Author("Michał", 0), new Author("Loredana", 1)), 0); + stream = article.getListOfAuthors().stream(); + split1 = Executor.generateElements().spliterator(); + split2 = split1.trySplit(); + spliterator = new RelatedAuthorSpliterator(article.getListOfAuthors()); + } + + @Test + public void givenAstreamOfAuthors_whenProcessedInParallelWithCustomSpliterator_coubtProducessRightOutput() { + Stream stream2 = StreamSupport.stream(spliterator, true); + assertThat(Executor.countAutors(stream2.parallel())).isEqualTo(9); + } + + @Test + public void givenSpliterator_whenAppliedToAListOfArticle_thenSplittedInHalf() { + assertThat(new Task(split1).call()).containsSequence(Executor.generateElements().size() / 2 + ""); + assertThat(new Task(split2).call()).containsSequence(Executor.generateElements().size() / 2 + ""); + } +} diff --git a/core-java-concurrency/README.md b/core-java-concurrency/README.md index 23509013d5..8122c71bcb 100644 --- a/core-java-concurrency/README.md +++ b/core-java-concurrency/README.md @@ -33,3 +33,4 @@ - [Daemon Threads in Java](http://www.baeldung.com/java-daemon-thread) - [Implementing a Runnable vs Extending a Thread](http://www.baeldung.com/java-runnable-vs-extending-thread) - [How to Kill a Java Thread](http://www.baeldung.com/java-thread-stop) +- [ExecutorService - Waiting for Threads to Finish](http://www.baeldung.com/java-executor-wait-for-threads) diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java index 9b850c4153..d9e7434e0c 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java @@ -1,5 +1,7 @@ package com.baeldung.concurrent.waitandnotify; +import org.slf4j.Logger; + public class Data { private String packet; @@ -11,7 +13,9 @@ public class Data { while (transfer) { try { wait(); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + System.out.println("Thread Interrupted"); + } } transfer = true; @@ -23,7 +27,9 @@ public class Data { while (!transfer) { try { wait(); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + System.out.println("Thread Interrupted"); + } } transfer = false; diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java index 63f48b8031..724e908a99 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java @@ -19,7 +19,9 @@ public class Receiver implements Runnable { //Thread.sleep() to mimic heavy server-side processing try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + System.out.println("Thread Interrupted"); + } } } } \ No newline at end of file diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java index b7d782c3f5..b4945b7b73 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java @@ -11,11 +11,11 @@ public class Sender implements Runnable { public void run() { String packets[] = { - "First packet", - "Second packet", - "Third packet", - "Fourth packet", - "End" + "First packet", + "Second packet", + "Third packet", + "Fourth packet", + "End" }; for (String packet : packets) { @@ -24,7 +24,9 @@ public class Sender implements Runnable { //Thread.sleep() to mimic heavy server-side processing try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + System.out.println("Thread Interrupted"); + } } } } \ No newline at end of file diff --git a/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java b/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java index 49f4313e9d..8ecc92236e 100644 --- a/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java +++ b/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java @@ -13,38 +13,38 @@ import org.junit.Test; public class NetworkIntegrationTest { - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); - private String expected; - - @Before - public void setUpStreams() { - System.setOut(new PrintStream(outContent)); - System.setErr(new PrintStream(errContent)); - } - - @Before - public void setUpExpectedOutput() { - StringWriter expectedStringWriter = new StringWriter(); - - PrintWriter printWriter = new PrintWriter(expectedStringWriter); - printWriter.println("First packet"); - printWriter.println("Second packet"); - printWriter.println("Third packet"); - printWriter.println("Fourth packet"); - printWriter.close(); - - expected = expectedStringWriter.toString(); - } + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private String expected; + + @Before + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @Before + public void setUpExpectedOutput() { + StringWriter expectedStringWriter = new StringWriter(); + + PrintWriter printWriter = new PrintWriter(expectedStringWriter); + printWriter.println("First packet"); + printWriter.println("Second packet"); + printWriter.println("Third packet"); + printWriter.println("Fourth packet"); + printWriter.close(); + + expected = expectedStringWriter.toString(); + } - @After - public void cleanUpStreams() { - System.setOut(null); - System.setErr(null); - } - - @Test - public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() { + @After + public void cleanUpStreams() { + System.setOut(null); + System.setErr(null); + } + + @Test + public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() { Data data = new Data(); Thread sender = new Thread(new Sender(data)); Thread receiver = new Thread(new Receiver(data)); @@ -54,12 +54,12 @@ public class NetworkIntegrationTest { //wait for sender and receiver to finish before we test against expected try { - sender.join(); - receiver.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - assertEquals(expected, outContent.toString()); - } + sender.join(); + receiver.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + assertEquals(expected, outContent.toString()); + } } diff --git a/core-java/README.md b/core-java/README.md index 27f210a0c3..ed60db855c 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -118,3 +118,12 @@ - [Implementing a Binary Tree in Java](http://www.baeldung.com/java-binary-tree) - [A Guide to ThreadLocalRandom in Java](http://www.baeldung.com/java-thread-local-random) - [RegEx for matching Date Pattern in Java](http://www.baeldung.com/java-date-regular-expressions) +- [Introduction to the JDBC RowSet Interface in Java](http://www.baeldung.com/java-jdbc-rowset) +- [Nested Classes in Java](http://www.baeldung.com/java-nested-classes) +- [A Guide to Java Loops](http://www.baeldung.com/java-loops) +- [Varargs in Java](http://www.baeldung.com/java-varargs) +- [A Guide to HashSet in Java](http://www.baeldung.com/java-hashset) +- [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces) +- [Polymorphism in Java](http://www.baeldung.com/java-polymorphism) +- [Recursion In Java](http://www.baeldung.com/java-recursion) +- [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize) diff --git a/core-java/src/main/java/com/baeldung/finalize/CloseableResource.java b/core-java/src/main/java/com/baeldung/finalize/CloseableResource.java new file mode 100644 index 0000000000..d286570fbc --- /dev/null +++ b/core-java/src/main/java/com/baeldung/finalize/CloseableResource.java @@ -0,0 +1,30 @@ +package com.baeldung.finalize; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class CloseableResource implements AutoCloseable { + private BufferedReader reader; + + public CloseableResource() { + InputStream input = this.getClass().getClassLoader().getResourceAsStream("file.txt"); + reader = new BufferedReader(new InputStreamReader(input)); + } + + public String readFirstLine() throws IOException { + String firstLine = reader.readLine(); + return firstLine; + } + + @Override + public void close() { + try { + reader.close(); + System.out.println("Closed BufferedReader in the close method"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java/src/main/java/com/baeldung/finalize/Finalizable.java b/core-java/src/main/java/com/baeldung/finalize/Finalizable.java new file mode 100644 index 0000000000..cfc6616f5c --- /dev/null +++ b/core-java/src/main/java/com/baeldung/finalize/Finalizable.java @@ -0,0 +1,30 @@ +package com.baeldung.finalize; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class Finalizable { + private BufferedReader reader; + + public Finalizable() { + InputStream input = this.getClass().getClassLoader().getResourceAsStream("file.txt"); + reader = new BufferedReader(new InputStreamReader(input)); + } + + public String readFirstLine() throws IOException { + String firstLine = reader.readLine(); + return firstLine; + } + + @Override + public void finalize() { + try { + reader.close(); + System.out.println("Closed BufferedReader in the finalizer"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java/src/main/java/com/baeldung/trie/Trie.java b/core-java/src/main/java/com/baeldung/trie/Trie.java new file mode 100644 index 0000000000..dd51d97b2d --- /dev/null +++ b/core-java/src/main/java/com/baeldung/trie/Trie.java @@ -0,0 +1,62 @@ +package com.baeldung.trie; + +public class Trie { + private TrieNode root; + + Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode current = root; + + for (int i = 0; i < word.length(); i++) { + current = current.getChildren().computeIfAbsent(word.charAt(i), c -> new TrieNode()); + } + current.setEndOfWord(true); + } + + public boolean delete(String word) { + return delete(root, word, 0); + } + + public boolean containsNode(String word) { + TrieNode current = root; + + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + TrieNode node = current.getChildren().get(ch); + if (node == null) { + return false; + } + current = node; + } + return current.isEndOfWord(); + } + + public boolean isEmpty() { + return root == null; + } + + private boolean delete(TrieNode current, String word, int index) { + if (index == word.length()) { + if (!current.isEndOfWord()) { + return false; + } + current.setEndOfWord(false); + return current.getChildren().isEmpty(); + } + char ch = word.charAt(index); + TrieNode node = current.getChildren().get(ch); + if (node == null) { + return false; + } + boolean shouldDeleteCurrentNode = delete(node, word, index + 1); + + if (shouldDeleteCurrentNode) { + current.getChildren().remove(ch); + return current.getChildren().isEmpty(); + } + return false; + } +} \ No newline at end of file diff --git a/core-java/src/main/java/com/baeldung/trie/TrieNode.java b/core-java/src/main/java/com/baeldung/trie/TrieNode.java new file mode 100644 index 0000000000..25dc753950 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/trie/TrieNode.java @@ -0,0 +1,31 @@ +package com.baeldung.trie; + +import java.util.HashMap; +import java.util.Map; + +class TrieNode { + private Map children; + private boolean endOfWord; + + public TrieNode() { + children = new HashMap<>(); + endOfWord = false; + } + + public Map getChildren() { + return children; + } + + public void setChildren(Map children) { + this.children = children; + } + + public boolean isEndOfWord() { + return endOfWord; + } + + public void setEndOfWord(boolean endOfWord) { + this.endOfWord = endOfWord; + } + +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/finalize/FinalizeUnitTest.java b/core-java/src/test/java/com/baeldung/finalize/FinalizeUnitTest.java new file mode 100644 index 0000000000..57d409074b --- /dev/null +++ b/core-java/src/test/java/com/baeldung/finalize/FinalizeUnitTest.java @@ -0,0 +1,23 @@ +package com.baeldung.finalize; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +public class FinalizeUnitTest { + @Test + public void whenGC_thenFinalizerExecuted() throws IOException { + String firstLine = new Finalizable().readFirstLine(); + Assert.assertEquals("baeldung.com", firstLine); + System.gc(); + } + + @Test + public void whenTryWResourcesExits_thenResourceClosed() throws IOException { + try (CloseableResource resource = new CloseableResource()) { + String firstLine = resource.readFirstLine(); + Assert.assertEquals("baeldung.com", firstLine); + } + } +} diff --git a/core-java/src/test/java/com/baeldung/trie/TrieTest.java b/core-java/src/test/java/com/baeldung/trie/TrieTest.java new file mode 100644 index 0000000000..be7e5575d8 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/trie/TrieTest.java @@ -0,0 +1,68 @@ +package com.baeldung.trie; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TrieTest { + + @Test + public void whenEmptyTrie_thenNoElements() { + Trie trie = new Trie(); + + assertFalse(trie.isEmpty()); + } + + @Test + public void givenATrie_whenAddingElements_thenTrieNotEmpty() { + Trie trie = createExampleTrie(); + + assertFalse(trie.isEmpty()); + } + + @Test + public void givenATrie_whenAddingElements_thenTrieHasThoseElements() { + Trie trie = createExampleTrie(); + + assertFalse(trie.containsNode("3")); + assertFalse(trie.containsNode("vida")); + + assertTrue(trie.containsNode("Programming")); + assertTrue(trie.containsNode("is")); + assertTrue(trie.containsNode("a")); + assertTrue(trie.containsNode("way")); + assertTrue(trie.containsNode("of")); + assertTrue(trie.containsNode("life")); + } + + @Test + public void givenATrie_whenLookingForNonExistingElement_thenReturnsFalse() { + Trie trie = createExampleTrie(); + + assertFalse(trie.containsNode("99")); + } + + @Test + public void givenATrie_whenDeletingElements_thenTreeDoesNotContainThoseElements() { + + Trie trie = createExampleTrie(); + + assertTrue(trie.containsNode("Programming")); + trie.delete("Programming"); + assertFalse(trie.containsNode("Programming")); + } + + private Trie createExampleTrie() { + Trie trie = new Trie(); + + trie.insert("Programming"); + trie.insert("is"); + trie.insert("a"); + trie.insert("way"); + trie.insert("of"); + trie.insert("life"); + + return trie; + } +} diff --git a/core-kotlin/README.md b/core-kotlin/README.md index 4b5f921f7b..5908d480b7 100644 --- a/core-kotlin/README.md +++ b/core-kotlin/README.md @@ -16,5 +16,5 @@ - [Delegated Properties in Kotlin](http://www.baeldung.com/kotlin-delegated-properties) - [Sealed Classes in Kotlin](http://www.baeldung.com/kotlin-sealed-classes) - [JUnit 5 for Kotlin Developers](http://www.baeldung.com/junit-5-kotlin) - +- [Extension Methods in Kotlin](http://www.baeldung.com/kotlin-extension-methods) diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml index 2cd5275eeb..33bdbf719f 100644 --- a/core-kotlin/pom.xml +++ b/core-kotlin/pom.xml @@ -49,6 +49,11 @@ kotlin-stdlib-jre8 ${kotlin-stdlib.version} + + khttp + khttp + 0.1.0 + org.jetbrains.kotlin kotlin-test-junit diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/khttp/KhttpTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/khttp/KhttpTest.kt new file mode 100644 index 0000000000..e9147c9489 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/khttp/KhttpTest.kt @@ -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) + } +} \ No newline at end of file diff --git a/dubbo/README.md b/dubbo/README.md index dec02f5cfc..0a4cd9a204 100644 --- a/dubbo/README.md +++ b/dubbo/README.md @@ -1,4 +1,4 @@ ## Relevant articles: -- [Intro to Dubbo](http://www.baeldung.com/dubbo-intro) +- [Introduction to Dubbo](http://www.baeldung.com/dubbo) diff --git a/flyway/README.MD b/flyway/README.MD index 1b3f3c05ee..daeb9012b5 100644 --- a/flyway/README.MD +++ b/flyway/README.MD @@ -1,2 +1,3 @@ ### Relevant Articles: - [Database Migrations with Flyway](http://www.baeldung.com/database-migrations-with-flyway) +- [A Guide to Flyway Callbacks](http://www.baeldung.com/flyway-callbacks) diff --git a/gradle/README.md b/gradle/README.md index dd5ea03a18..5fc7b40e3f 100644 --- a/gradle/README.md +++ b/gradle/README.md @@ -1,2 +1,4 @@ ## Relevant articles: - [Introduction to Gradle](http://www.baeldung.com/gradle) +- [Writing Custom Gradle Plugins](http://www.baeldung.com/gradle-create-plugin) +- [Creating a Fat Jar in Gradle](http://www.baeldung.com/gradle-fat-jar) diff --git a/guest/deep-jsf/README.md b/guest/deep-jsf/README.md new file mode 100644 index 0000000000..b5f532535b --- /dev/null +++ b/guest/deep-jsf/README.md @@ -0,0 +1,15 @@ +## Building + +To build the module, use Maven's `package` goal: + +``` +mvn clean package +``` + +The `war` file will be available at `target/deep-jsf.war` + +## Running + +The `war` application is deployed to a Java EE 7 compliant application server, for example, to GlassFish 4 or later. + +The example then will be accessible at http://localhost:8080/deep-jsf/index.xhtml \ No newline at end of file diff --git a/guest/deep-jsf/pom.xml b/guest/deep-jsf/pom.xml new file mode 100644 index 0000000000..68801ba010 --- /dev/null +++ b/guest/deep-jsf/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + com.stackify + deep-jsf + 0.0.1-SNAPSHOT + war + + + false + + + + + + javax + javaee-api + 7.0 + provided + + + + + + deep-jsf + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/guest/deep-jsf/src/main/java/com/stackify/deepjsf/GreetControllerBean.java b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/GreetControllerBean.java new file mode 100644 index 0000000000..7f5cf99781 --- /dev/null +++ b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/GreetControllerBean.java @@ -0,0 +1,14 @@ +package com.stackify.deepjsf; + +import javax.faces.bean.ManagedBean; +import javax.faces.bean.RequestScoped; + +@ManagedBean +@RequestScoped +public class GreetControllerBean { + + public String greet() { + return "greet"; + } + +} diff --git a/guest/deep-jsf/src/main/java/com/stackify/deepjsf/PhaseListenerBean.java b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/PhaseListenerBean.java new file mode 100644 index 0000000000..d4f6a6e815 --- /dev/null +++ b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/PhaseListenerBean.java @@ -0,0 +1,47 @@ +package com.stackify.deepjsf; + +import javax.faces.bean.ManagedBean; +import javax.faces.bean.RequestScoped; +import javax.faces.component.UIComponent; +import javax.faces.component.UIViewRoot; +import javax.faces.component.visit.VisitContext; +import javax.faces.component.visit.VisitResult; +import javax.faces.event.PhaseEvent; +import javax.faces.event.PhaseId; +import javax.servlet.http.HttpServletRequest; + +@ManagedBean +@RequestScoped +public class PhaseListenerBean { + + public void beforeListener(PhaseEvent event) { + if (!event.getPhaseId().equals(PhaseId.RENDER_RESPONSE)) { + return; + } + UIViewRoot root = event.getFacesContext().getViewRoot(); + + boolean showNewFeature = showNewFeatureForIp(event); + + processComponentTree(root, event, showNewFeature); + } + + private boolean showNewFeatureForIp(PhaseEvent event) { + HttpServletRequest request = (HttpServletRequest) event.getFacesContext() + .getExternalContext().getRequest(); + String ip = request.getRemoteAddr(); + return !ip.startsWith("127.0"); + } + + private void processComponentTree(UIComponent component, PhaseEvent event, boolean show) { + component.visitTree(VisitContext.createVisitContext(event.getFacesContext()), + (context, target) -> { + if (target.getId() != null + && target.getId().startsWith("new-feature-") + && !show) { + target.setRendered(false); + } + return VisitResult.ACCEPT; + }); + } + +} diff --git a/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserBean.java b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserBean.java new file mode 100644 index 0000000000..f6c94e87b8 --- /dev/null +++ b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserBean.java @@ -0,0 +1,48 @@ +package com.stackify.deepjsf; + +import javax.faces.bean.ManagedBean; +import javax.faces.bean.SessionScoped; +import javax.faces.event.ValueChangeEvent; + +@ManagedBean +@SessionScoped +public class UserBean { + + private String name = ""; + + private String lastName = ""; + + private String proposedLogin = ""; + + public void nameChanged(ValueChangeEvent event) { + this.proposedLogin = event.getNewValue() + "-" + lastName; + } + + public void lastNameChanged(ValueChangeEvent event) { + this.proposedLogin = name + "-" + event.getNewValue(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getProposedLogin() { + return proposedLogin; + } + + public void setProposedLogin(String proposedLogin) { + this.proposedLogin = proposedLogin; + } +} diff --git a/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserControllerBean.java b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserControllerBean.java new file mode 100644 index 0000000000..c2a46a019a --- /dev/null +++ b/guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserControllerBean.java @@ -0,0 +1,14 @@ +package com.stackify.deepjsf; + +import javax.faces.bean.ManagedBean; +import javax.faces.bean.RequestScoped; + +@ManagedBean +@RequestScoped +public class UserControllerBean { + + public String register() { + return "register-success"; + } + +} diff --git a/guest/deep-jsf/src/main/webapp/WEB-INF/faces-config.xml b/guest/deep-jsf/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000000..264e60065c --- /dev/null +++ b/guest/deep-jsf/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,15 @@ + + + + + /register.xhtml + + register-success + /hello.xhtml + + + + \ No newline at end of file diff --git a/guest/deep-jsf/src/main/webapp/greet.xhtml b/guest/deep-jsf/src/main/webapp/greet.xhtml new file mode 100644 index 0000000000..50c79c64e1 --- /dev/null +++ b/guest/deep-jsf/src/main/webapp/greet.xhtml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/guest/deep-jsf/src/main/webapp/hello.xhtml b/guest/deep-jsf/src/main/webapp/hello.xhtml new file mode 100644 index 0000000000..f9c3745dca --- /dev/null +++ b/guest/deep-jsf/src/main/webapp/hello.xhtml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/guest/deep-jsf/src/main/webapp/index.xhtml b/guest/deep-jsf/src/main/webapp/index.xhtml new file mode 100644 index 0000000000..de99b89815 --- /dev/null +++ b/guest/deep-jsf/src/main/webapp/index.xhtml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/guest/deep-jsf/src/main/webapp/register.xhtml b/guest/deep-jsf/src/main/webapp/register.xhtml new file mode 100644 index 0000000000..ba1b8e0233 --- /dev/null +++ b/guest/deep-jsf/src/main/webapp/register.xhtml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate5/README.md b/hibernate5/README.md index 1eb090f05d..5143b097ce 100644 --- a/hibernate5/README.md +++ b/hibernate5/README.md @@ -5,3 +5,5 @@ - [Hibernate – Mapping Date and Time](http://www.baeldung.com/hibernate-date-time) - [Hibernate Inheritance Mapping](http://www.baeldung.com/hibernate-inheritance) - [A Guide to Multitenancy in Hibernate 5](http://www.baeldung.com/hibernate-5-multitenancy) +- [Introduction to Hibernate Spatial](http://www.baeldung.com/hibernate-spatial) +- [Hibernate Interceptors](http://www.baeldung.com/hibernate-interceptor) diff --git a/influxdb/README.md b/influxdb/README.md index f2c421580e..7d1684688d 100644 --- a/influxdb/README.md +++ b/influxdb/README.md @@ -2,6 +2,7 @@ ### Relevant Article: - [Introduction to using InfluxDB with Java](http://www.baeldung.com/using-influxdb-with-java/) +- [Using InfluxDB with Java](http://www.baeldung.com/java-influxdb) ### Overview This Maven project contains the Java code for the article linked above. diff --git a/java-rmi/pom.xml b/java-rmi/pom.xml new file mode 100644 index 0000000000..7c08968cbf --- /dev/null +++ b/java-rmi/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + com.baeldung.rmi + java-rmi + 1.0-SNAPSHOT + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + UTF-8 + + + diff --git a/java-rmi/src/main/java/com/baeldung/rmi/Message.java b/java-rmi/src/main/java/com/baeldung/rmi/Message.java new file mode 100644 index 0000000000..6d21a45fe3 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/Message.java @@ -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; + } + +} diff --git a/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java b/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java new file mode 100644 index 0000000000..f962e4ad07 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java @@ -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; +} \ No newline at end of file diff --git a/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java b/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java new file mode 100644 index 0000000000..ebf03d4b67 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java @@ -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; + } + +} \ No newline at end of file diff --git a/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java b/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java new file mode 100644 index 0000000000..66bfbe49eb --- /dev/null +++ b/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java @@ -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"); + } + } + +} \ No newline at end of file diff --git a/jmeter/README.md b/jmeter/README.md index dec8364647..e3f9d1a4db 100644 --- a/jmeter/README.md +++ b/jmeter/README.md @@ -38,4 +38,9 @@ $ curl -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Dassi", " Now with default configurations it will be available at: [http://localhost:8080](http://localhost:8080) -Enjoy it :) \ No newline at end of file +Enjoy it :) + +### Relevant Articles: + +- [Intro to Performance Testing using JMeter](http://www.baeldung.com/jmeter) +- [Configure Jenkins to Run and Show JMeter Tests](http://www.baeldung.com/jenkins-and-jmeter) diff --git a/libraries-data/README.md b/libraries-data/README.md index ceb0a1d5f7..9e8d32fa44 100644 --- a/libraries-data/README.md +++ b/libraries-data/README.md @@ -2,3 +2,4 @@ - [Introduction to Reladomo](http://www.baeldung.com/reladomo) - [Introduction to ORMLite](http://www.baeldung.com/ormlite) - [Introduction To Kryo](http://www.baeldung.com/kryo) +- [Introduction to KafkaStreams in Java](http://www.baeldung.com/java-kafka-streams) diff --git a/libraries/README.md b/libraries/README.md index 990a50e711..d001f13698 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -58,6 +58,8 @@ - [Introduction to BouncyCastle with Java](http://www.baeldung.com/java-bouncy-castle) - [Intro to JDO Queries 2/2](http://www.baeldung.com/jdo-queries) - [Guide to google-http-client](http://www.baeldung.com/google-http-client) +- [Interact with Google Sheets from Java](http://www.baeldung.com/google-sheets-java-client) + The libraries module contains examples related to small libraries that are relatively easy to use and does not require any separate module of its own. diff --git a/libraries/pom.xml b/libraries/pom.xml index 09c8cb8335..3e802e76c8 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -194,6 +194,11 @@ jetty-servlet ${jetty.version} + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + rome rome @@ -741,12 +746,11 @@ 3.6.2 1.5.0 3.1.0 - 9.4.3.v20170317 4.5.3 2.5 1.6 1.4.196 - 9.4.2.v20170220 + 9.4.8.v20171121 4.5.3 2.5 1.2.0 diff --git a/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java b/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java new file mode 100644 index 0000000000..00ba84368a --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java @@ -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; + } + +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java b/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java new file mode 100644 index 0000000000..a38759c903 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java @@ -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) { + } + +} diff --git a/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java new file mode 100644 index 0000000000..c919bdb09c --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java @@ -0,0 +1,22 @@ +package com.baeldung.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.util.logging.Logger; + +public class ChannelHandlerA extends ChannelInboundHandlerAdapter { + + private Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + throw new Exception("Ooops"); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.info("Exception Occurred in ChannelHandler A"); + ctx.fireExceptionCaught(cause); + } +} diff --git a/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java new file mode 100644 index 0000000000..c5bdeb1013 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java @@ -0,0 +1,20 @@ +package com.baeldung.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.util.logging.Logger; + + +public class ChannelHandlerB extends ChannelInboundHandlerAdapter { + + private Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.info("Exception Handled in ChannelHandler B"); + logger.info(cause.getLocalizedMessage()); + //do more exception handling + ctx.close(); + } +} diff --git a/libraries/src/main/java/com/baeldung/netty/NettyServerB.java b/libraries/src/main/java/com/baeldung/netty/NettyServerB.java new file mode 100644 index 0000000000..c8004623c2 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/NettyServerB.java @@ -0,0 +1,47 @@ +package com.baeldung.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +public class NettyServerB { + + private int port; + + private NettyServerB(int port) { + this.port = port; + } + + private void run() throws Exception { + + EventLoopGroup bossGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new ChannelHandlerA(), new ChannelHandlerB()); + } + }) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + ChannelFuture f = b.bind(port).sync(); // (7) + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new NettyServerB(8080).run(); + } +} diff --git a/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java b/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java new file mode 100644 index 0000000000..849ba24f55 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java @@ -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(); + } + +} diff --git a/libraries/src/test/java/com/baeldung/pact/PactConsumerDrivenContractUnitTest.java b/libraries/src/test/java/com/baeldung/pact/PactConsumerDrivenContractUnitTest.java index b152b22964..cc3b441aa4 100644 --- a/libraries/src/test/java/com/baeldung/pact/PactConsumerDrivenContractUnitTest.java +++ b/libraries/src/test/java/com/baeldung/pact/PactConsumerDrivenContractUnitTest.java @@ -32,9 +32,9 @@ public class PactConsumerDrivenContractUnitTest { headers.put("Content-Type", "application/json"); return builder - .given("test GET ") + .given("test GET") .uponReceiving("GET REQUEST") - .path("/") + .path("/pact") .method("GET") .willRespondWith() .status(200) @@ -45,11 +45,9 @@ public class PactConsumerDrivenContractUnitTest { .method("POST") .headers(headers) .body("{\"name\": \"Michael\"}") - .path("/create") + .path("/pact") .willRespondWith() .status(201) - .headers(headers) - .body("") .toPact(); } @@ -59,7 +57,7 @@ public class PactConsumerDrivenContractUnitTest { public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() { //when ResponseEntity response - = new RestTemplate().getForEntity(mockProvider.getUrl(), String.class); + = new RestTemplate().getForEntity(mockProvider.getUrl() + "/pact", String.class); //then assertThat(response.getStatusCode().value()).isEqualTo(200); @@ -73,7 +71,7 @@ public class PactConsumerDrivenContractUnitTest { //when ResponseEntity postResponse = new RestTemplate().exchange( - mockProvider.getUrl() + "/create", + mockProvider.getUrl() + "/pact", HttpMethod.POST, new HttpEntity<>(jsonBody, httpHeaders), String.class diff --git a/libraries/src/test/resources/jetty-embedded-demo-app.war b/libraries/src/test/resources/jetty-embedded-demo-app.war new file mode 100644 index 0000000000..7f8bc37df0 Binary files /dev/null and b/libraries/src/test/resources/jetty-embedded-demo-app.war differ diff --git a/logging-modules/README.md b/logging-modules/README.md index 23458cf30b..8ae7316047 100644 --- a/logging-modules/README.md +++ b/logging-modules/README.md @@ -1,3 +1,6 @@ ## Logging Modules +### Relevant Articles: + +- [Creating a Custom Logback Appender](http://www.baeldung.com/custom-logback-appender) diff --git a/logging-modules/log4j2/pom.xml b/logging-modules/log4j2/pom.xml index 48608fbc80..46b8b80597 100644 --- a/logging-modules/log4j2/pom.xml +++ b/logging-modules/log4j2/pom.xml @@ -59,10 +59,10 @@ - 2.8.5 + 2.9.3 1.4.193 2.1.1 - 2.7 + 2.10.0 diff --git a/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java new file mode 100644 index 0000000000..9493c32094 --- /dev/null +++ b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java @@ -0,0 +1,45 @@ +package com.baeldung.logging.log4j2.tests; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JSONLayoutTest { + + private static Logger logger; + private ByteArrayOutputStream consoleOutput = new ByteArrayOutputStream(); + private PrintStream ps = new PrintStream(consoleOutput); + + @Before + public void setUp() { + // Redirect console output to our stream + System.setOut(ps); + logger = LogManager.getLogger("CONSOLE_JSON_APPENDER"); + } + + @Test + public void whenLogLayoutInJSON_thenOutputIsCorrectJSON() { + logger.debug("Debug message"); + String currentLog = consoleOutput.toString(); + assertTrue(!currentLog.isEmpty() && isValidJSON(currentLog)); + } + + public static boolean isValidJSON(String jsonInString) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(jsonInString); + return true; + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/logging-modules/log4j2/src/test/resources/log4j2.xml b/logging-modules/log4j2/src/test/resources/log4j2.xml index 21fd1da446..4dcb7cce5a 100644 --- a/logging-modules/log4j2/src/test/resources/log4j2.xml +++ b/logging-modules/log4j2/src/test/resources/log4j2.xml @@ -1,16 +1,25 @@ - + - + - + - + + + + + + @@ -19,53 +28,58 @@ - + - + - + - + - + - + - + - + - + + + + diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml index 8169134442..cd0d3758cc 100644 --- a/logging-modules/logback/pom.xml +++ b/logging-modules/logback/pom.xml @@ -1,6 +1,6 @@ 4.0.0 @@ -12,6 +12,8 @@ UTF-8 1.2.3 + 0.1.5 + 2.9.3 @@ -28,7 +30,21 @@ logback-classic ${logback.version} - + + ch.qos.logback.contrib + logback-json-classic + ${logback.contrib.version} + + + ch.qos.logback.contrib + logback-jackson + ${logback.contrib.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + diff --git a/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java b/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java new file mode 100644 index 0000000000..ca3c4b3457 --- /dev/null +++ b/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java @@ -0,0 +1,45 @@ +package com.baeldung.logback; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JSONLayoutTest { + + private static Logger logger; + private ByteArrayOutputStream consoleOutput = new ByteArrayOutputStream(); + private PrintStream ps = new PrintStream(consoleOutput); + + @Before + public void setUp() { + // Redirect console output to our stream + System.setOut(ps); + logger = LoggerFactory.getLogger("jsonLogger"); + } + + @Test + public void whenLogLayoutInJSON_thenOutputIsCorrectJSON() { + logger.debug("Debug message"); + String currentLog = consoleOutput.toString(); + assertTrue(!currentLog.isEmpty() && isValidJSON(currentLog)); + } + + public static boolean isValidJSON(String jsonInString) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(jsonInString); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/logging-modules/logback/src/test/resources/logback-test.xml b/logging-modules/logback/src/test/resources/logback-test.xml index 8254e6ac80..df81f18cc2 100644 --- a/logging-modules/logback/src/test/resources/logback-test.xml +++ b/logging-modules/logback/src/test/resources/logback-test.xml @@ -1,14 +1,31 @@ - + + + test - + + + # JSON appender + + + + true + + yyyy-MM-dd' 'HH:mm:ss.SSS + + + + + + - - + + \ No newline at end of file diff --git a/lucene/README.md b/lucene/README.md new file mode 100644 index 0000000000..b1d33472f4 --- /dev/null +++ b/lucene/README.md @@ -0,0 +1,4 @@ +### Relevant Articles: + +- [Introduction to Apache Lucene](http://www.baeldung.com/lucene) +- [A Simple File Search with Lucene](http://www.baeldung.com/lucene-file-search) diff --git a/muleesb/README.md b/muleesb/README.md new file mode 100644 index 0000000000..8da4e595e3 --- /dev/null +++ b/muleesb/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Getting Started With Mule ESB](http://www.baeldung.com/mule-esb) diff --git a/orientdb/README.md b/orientdb/README.md index 152ad392dd..56dfe0ab11 100644 --- a/orientdb/README.md +++ b/orientdb/README.md @@ -22,4 +22,7 @@ $ mvn clean install Before launching unit tests: - Install OrientDB - Create BaeldungDB, BaeldungDBTwo and BaeldungDBThree databases -- Uncomment annotations on the test files \ No newline at end of file +- Uncomment annotations on the test files + +### Relevant Articles: +- [Introduction to the OrientDB Java APIs](http://www.baeldung.com/java-orientdb) diff --git a/persistence-modules/README.md b/persistence-modules/README.md index 6c2b1d2ca9..13e7f731aa 100644 --- a/persistence-modules/README.md +++ b/persistence-modules/README.md @@ -1,3 +1,7 @@ ## Persistence Modules + +### Relevant Articles: + +- [Introduction to Hibernate Search](http://www.baeldung.com/hibernate-search) diff --git a/pom.xml b/pom.xml index 9bf12725c0..4a25459fcb 100644 --- a/pom.xml +++ b/pom.xml @@ -87,8 +87,9 @@ jackson vavr - java-lite - java-vavr-stream + java-lite + java-rmi + java-vavr-stream javax-servlets javaxval jaxb @@ -113,7 +114,9 @@ lombok mapstruct + mesos-marathon testing-modules/mockito testing-modules/mockito-2 diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml index 36eaaa1530..5065457c4b 100644 --- a/spring-5-reactive/pom.xml +++ b/spring-5-reactive/pom.xml @@ -60,7 +60,13 @@ - + + + org.projectlombok + lombok + compile + + org.apache.geronimo.specs geronimo-json_1.1_spec diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/Event.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java similarity index 79% rename from spring-reactive-websocket/src/main/java/com/baeldung/Event.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java index 20d678c214..90f83a566f 100644 --- a/spring-reactive-websocket/src/main/java/com/baeldung/Event.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.reactive.websocket; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java new file mode 100644 index 0000000000..c9a333c044 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java @@ -0,0 +1,26 @@ +package com.baeldung.reactive.websocket; + +import java.net.URI; +import java.time.Duration; +import org.springframework.web.reactive.socket.WebSocketMessage; +import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; +import org.springframework.web.reactive.socket.client.WebSocketClient; + +import reactor.core.publisher.Mono; + +public class ReactiveJavaClientWebSocket { + public static void main(String[] args) throws InterruptedException { + + WebSocketClient client = new ReactorNettyWebSocketClient(); + client.execute( + URI.create("ws://localhost:8080/event-emitter"), + session -> session.send( + Mono.just(session.textMessage("event-spring-reactive-client-websocket"))) + .thenMany(session.receive() + .map(WebSocketMessage::getPayloadAsText) + .log()) + .then()) + .block(Duration.ofSeconds(10L)); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java new file mode 100644 index 0000000000..43b5e50387 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.reactive.websocket; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ReactiveWebSocketApplication { + public static void main(String[] args) { + SpringApplication.run(ReactiveWebSocketApplication.class, args); + } +} diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java similarity index 96% rename from spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java index 6729e09273..974def5a91 100644 --- a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.reactive.websocket; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java new file mode 100644 index 0000000000..669c212fd3 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java @@ -0,0 +1,48 @@ +package com.baeldung.reactive.websocket; + +import org.springframework.web.reactive.socket.WebSocketSession; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.WebSocketMessage; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.UUID; + +@Component +public class ReactiveWebSocketHandler implements WebSocketHandler { + + private Flux eventFlux = Flux.generate(e -> { + Event event = new Event(UUID.randomUUID().toString(), LocalDateTime.now().toString()); + e.next(event); + }); + + private Flux intervalFlux = Flux.interval(Duration.ofMillis(1000L)).zipWith(eventFlux, (time, event) -> event); + + private ObjectMapper json = new ObjectMapper(); + + @Override + public Mono handle(WebSocketSession webSocketSession) { + + return webSocketSession.send(intervalFlux.map(event -> { + try { + String jsonEvent = json.writeValueAsString(event); + System.out.println(jsonEvent); + return jsonEvent; + } catch (JsonProcessingException e) { + e.printStackTrace(); + return ""; + } + }).map(webSocketSession::textMessage)) + + .and(webSocketSession.receive().map(WebSocketMessage::getPayloadAsText).log()); + } + +} diff --git a/spring-reactive-websocket/src/main/resources/static/client-websocket.html b/spring-5-reactive/src/main/resources/static/client-websocket.html similarity index 100% rename from spring-reactive-websocket/src/main/resources/static/client-websocket.html rename to spring-5-reactive/src/main/resources/static/client-websocket.html diff --git a/spring-5-security/pom.xml b/spring-5-security/pom.xml index c0f73b1bdd..0a1d1f5df0 100644 --- a/spring-5-security/pom.xml +++ b/spring-5-security/pom.xml @@ -30,6 +30,10 @@ org.springframework.boot spring-boot-starter-thymeleaf + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + @@ -40,6 +44,21 @@ org.springframework.security spring-security-oauth2-jose + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..2a5c5f0368 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java @@ -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); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java new file mode 100644 index 0000000000..50995169a1 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java @@ -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 authorities) { + super(principal, credentials, authorities); + this.domain = domain; + super.setAuthenticated(true); // must use super, as we override + } + + public String getDomain() { + return this.domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java new file mode 100644 index 0000000000..693900d843 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java @@ -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; + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java new file mode 100644 index 0000000000..358129173d --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java @@ -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; + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java new file mode 100644 index 0000000000..ea979e2fab --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java @@ -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; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java new file mode 100644 index 0000000000..428c8bf532 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.loginextrafieldscustom; + +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 CustomUserRepository implements UserRepository { + + @Override + public User findUser(String username, String domain) { + if (StringUtils.isAnyBlank(username, domain)) { + return null; + } else { + Collection authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..0cf934f288 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.loginextrafieldscustom; + +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); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java new file mode 100644 index 0000000000..def85ab978 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java @@ -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(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java new file mode 100644 index 0000000000..aa03f15b6a --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java @@ -0,0 +1,23 @@ +package com.baeldung.loginextrafieldscustom; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class User extends org.springframework.security.core.userdetails.User { + + private static final long serialVersionUID = 1L; + + private String domain; + + public User(String username, String domain, String password, boolean enabled, + boolean accountNonExpired, boolean credentialsNonExpired, + boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java new file mode 100644 index 0000000000..e2358e055b --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.loginextrafieldscustom; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java new file mode 100644 index 0000000000..b5e0b511ac --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.loginextrafieldscustom; + +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 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); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..c82a13de1a --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java @@ -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); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java new file mode 100644 index 0000000000..d8c5ea8147 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java @@ -0,0 +1,65 @@ +package com.baeldung.loginextrafieldssimple; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +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.core.userdetails.UserDetailsService; +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 UserDetailsService 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 SimpleAuthenticationFilter authenticationFilter() throws Exception { + SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManagerBean()); + filter.setAuthenticationFailureHandler(failureHandler()); + return filter; + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authProvider()); + } + + public AuthenticationProvider authProvider() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder()); + return provider; + } + + public SimpleUrlAuthenticationFailureHandler failureHandler() { + return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); + } + + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java new file mode 100644 index 0000000000..9dcb524157 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java @@ -0,0 +1,54 @@ +package com.baeldung.loginextrafieldssimple; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class SimpleAuthenticationFilter 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()); + } + + UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request); + setDetails(request, authRequest); + return this.getAuthenticationManager() + .authenticate(authRequest); + } + + private UsernamePasswordAuthenticationToken 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 = ""; + } + + String usernameDomain = String.format("%s%s%s", username.trim(), + String.valueOf(Character.LINE_SEPARATOR), domain); + return new UsernamePasswordAuthenticationToken(usernameDomain, password); + } + + private String obtainDomain(HttpServletRequest request) { + return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java new file mode 100644 index 0000000000..2fad50ad01 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java @@ -0,0 +1,32 @@ +package com.baeldung.loginextrafieldssimple; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service("userDetailsService") +public class SimpleUserDetailsService implements UserDetailsService { + + private UserRepository userRepository; + + public SimpleUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + String[] usernameAndDomain = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR)); + if (usernameAndDomain == null || usernameAndDomain.length != 2) { + throw new UsernameNotFoundException("Username and domain must be provided"); + } + User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]); + if (user == null) { + throw new UsernameNotFoundException( + String.format("Username not found for domain, username=%s, domain=%s", + usernameAndDomain[0], usernameAndDomain[1])); + } + return user; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java new file mode 100644 index 0000000000..e8aaa774a1 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java @@ -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 authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java new file mode 100644 index 0000000000..b76da65638 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java @@ -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 authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java new file mode 100644 index 0000000000..919e611b9c --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.loginextrafieldssimple; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java new file mode 100644 index 0000000000..1b17de7bec --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java @@ -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 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); + } +} diff --git a/spring-5-security/src/main/resources/application-extrafields.properties b/spring-5-security/src/main/resources/application-extrafields.properties new file mode 100644 index 0000000000..ab4134ce3e --- /dev/null +++ b/spring-5-security/src/main/resources/application-extrafields.properties @@ -0,0 +1 @@ +spring.thymeleaf.prefix = classpath:/templatesextrafields/ \ No newline at end of file diff --git a/spring-5-security/src/main/resources/static/css/main.css b/spring-5-security/src/main/resources/static/css/main.css new file mode 100644 index 0000000000..febc353af7 --- /dev/null +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -0,0 +1,8 @@ +p.error { + font-weight: bold; + color: red; +} + +div.logout { + margin-right: 2em;; +} diff --git a/spring-5-security/src/main/resources/templatesextrafields/index.html b/spring-5-security/src/main/resources/templatesextrafields/index.html new file mode 100644 index 0000000000..37833ff0d2 --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -0,0 +1,32 @@ + + + + Spring Security with Extra Fields + + + + + + + + + +
+
+

Logged in: | Some Domain +

+
+
+ +
+
+
+ +

Hello Spring Security

+

This is an unsecured page, but you can access the secured pages after authenticating.

+ +
+ + diff --git a/spring-5-security/src/main/resources/templatesextrafields/login.html b/spring-5-security/src/main/resources/templatesextrafields/login.html new file mode 100644 index 0000000000..5c51ea3b2c --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -0,0 +1,36 @@ + + + + Login page + + + + + + + + + +
+ +
+ + diff --git a/spring-5-security/src/main/resources/templatesextrafields/user/index.html b/spring-5-security/src/main/resources/templatesextrafields/user/index.html new file mode 100644 index 0000000000..9c41f0e78c --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -0,0 +1,20 @@ + + + + Secured Page + + + + + + + + + +
+
+

This is a secured page!

+

Back to home page

+
+ + diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java new file mode 100644 index 0000000000..30b869714f --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java @@ -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")); + } +} diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java new file mode 100644 index 0000000000..38c219cb5e --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java @@ -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.loginextrafieldscustom.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldscustom.User; + +@RunWith(SpringRunner.class) +@SpringJUnitWebConfig +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsFullTest 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 authorities = new ArrayList<>(); + return new User("myusername", "mydomain", "password", true, true, true, true, authorities); + } + +} diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java new file mode 100644 index 0000000000..5c0d462772 --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java @@ -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 authorities = new ArrayList<>(); + return new User("myusername", "mydomain", "password", true, true, true, true, authorities); + } + +} diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index d6ee022522..a557604bf3 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -167,6 +167,12 @@ org.apache.activemq artemis-server + + + com.rometools + rome + ${rome.version} + @@ -276,6 +282,7 @@ 8.5.11 1.4.194 2.4.1.Final + 1.9.0 \ No newline at end of file diff --git a/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java index 59f7fd3ba5..478e8d393a 100644 --- a/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java +++ b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java @@ -6,14 +6,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; 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.WebMvcConfigurerAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @Configuration -@EnableWebMvc @ComponentScan(basePackages = "com.baeldung.internationalization.config") public class MvcConfig extends WebMvcConfigurerAdapter { @@ -33,6 +31,6 @@ public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(localeChangeInterceptor()); + registry.addInterceptor(localeChangeInterceptor()); } } diff --git a/spring-boot/src/main/java/com/baeldung/kong/QueryController.java b/spring-boot/src/main/java/com/baeldung/kong/QueryController.java new file mode 100644 index 0000000000..af63a7e8a1 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/kong/QueryController.java @@ -0,0 +1,32 @@ +package com.baeldung.kong; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author aiet + */ +@RestController +@RequestMapping("/stock") +public class QueryController { + + private static int REQUEST_COUNTER = 0; + + @GetMapping("/reqcount") + public int getReqCount(){ + return REQUEST_COUNTER; + } + + @GetMapping("/{code}") + public String getStockPrice(@PathVariable String code){ + REQUEST_COUNTER++; + if("BTC".equalsIgnoreCase(code)) + return "10000"; + else return "N/A"; + } + + + +} diff --git a/spring-boot/src/main/java/com/baeldung/kong/StockApp.java b/spring-boot/src/main/java/com/baeldung/kong/StockApp.java new file mode 100644 index 0000000000..f901592938 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/kong/StockApp.java @@ -0,0 +1,13 @@ +package com.baeldung.kong; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StockApp { + + public static void main(String[] args) { + SpringApplication.run(StockApp.class, args); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java b/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java new file mode 100644 index 0000000000..3efa619409 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java @@ -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 buildFeedItems(Map map, HttpServletRequest httpStRequest, HttpServletResponse httpStResponse) throws Exception { + List list = new ArrayList(); + + 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; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java b/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java new file mode 100644 index 0000000000..a3fbc4a37e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java @@ -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"; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java b/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java new file mode 100644 index 0000000000..ee36ecdc51 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java @@ -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(""); + } + +} \ No newline at end of file diff --git a/spring-boot/src/main/java/com/baeldung/rss/RssApp.java b/spring-boot/src/main/java/com/baeldung/rss/RssApp.java new file mode 100644 index 0000000000..2add7ed421 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/RssApp.java @@ -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); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java b/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java index 7c66391bd2..8c7f2f932a 100644 --- a/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java +++ b/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java @@ -4,7 +4,6 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.ServletRequestUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; diff --git a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java index 8704b42166..5038c7e5f7 100644 --- a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java +++ b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java @@ -2,7 +2,6 @@ package com.baeldung.webjar; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; import com.baeldung.autoconfiguration.MySQLAutoconfiguration; diff --git a/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java b/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java index decd8ac5db..7bcbfee45b 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java +++ b/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java @@ -5,7 +5,6 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import java.math.BigDecimal; -import java.math.MathContext; import java.util.Set; public class GenericBigDecimalConverter implements GenericConverter { diff --git a/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java b/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java index ad921c2c43..a6e0400845 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java +++ b/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java @@ -1,7 +1,6 @@ package org.baeldung.boot.converter.controller; import com.baeldung.toggle.Employee; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java index 0ab4ecb128..f9bdb67a10 100644 --- a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java +++ b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java @@ -11,7 +11,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; 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.RestController; diff --git a/spring-boot/src/main/resources/application.properties b/spring-boot/src/main/resources/application.properties index 458b4e0d46..059a6c96be 100644 --- a/spring-boot/src/main/resources/application.properties +++ b/spring-boot/src/main/resources/application.properties @@ -1,4 +1,4 @@ -server.port=8080 +server.port=9090 server.contextPath=/springbootapp management.port=8081 management.address=127.0.0.1 diff --git a/spring-boot/src/main/resources/static/internationalization.js b/spring-boot/src/main/resources/static/internationalization.js new file mode 100644 index 0000000000..ae416f083d --- /dev/null +++ b/spring-boot/src/main/resources/static/internationalization.js @@ -0,0 +1,8 @@ +$(document).ready(function() { + $("#locales").change(function () { + var selectedOption = $('#locales').val(); + if (selectedOption != ''){ + window.location.replace('international?lang=' + selectedOption); + } + }); +}); \ No newline at end of file diff --git a/spring-boot/src/main/resources/templates/international.html b/spring-boot/src/main/resources/templates/international.html index a2a5fbb591..e0cfb5143b 100644 --- a/spring-boot/src/main/resources/templates/international.html +++ b/spring-boot/src/main/resources/templates/international.html @@ -4,16 +4,7 @@ Home - +

diff --git a/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java b/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java new file mode 100644 index 0000000000..f399806a65 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java @@ -0,0 +1,165 @@ +package com.baeldung.kong; + +import com.baeldung.kong.domain.APIObject; +import com.baeldung.kong.domain.ConsumerObject; +import com.baeldung.kong.domain.KeyAuthObject; +import com.baeldung.kong.domain.PluginObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + +/** + * @author aiet + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = DEFINED_PORT, classes = StockApp.class) +public class KongAdminAPILiveTest { + + private String getStockPrice(String code) { + try { + return restTemplate.getForObject(new URI("http://localhost:8080/stock/" + code), String.class); + } catch (Exception ignored) { + } + return null; + } + + @Before + public void init() { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + } + + @Autowired TestRestTemplate restTemplate; + + @Test + public void givenEndpoint_whenQueryStockPrice_thenPriceCorrect() { + String response = getStockPrice("btc"); + assertEquals("10000", response); + + response = getStockPrice("eth"); + assertEquals("N/A", response); + } + + @Test + public void givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong() throws Exception { + restTemplate.delete("http://localhost:8001/apis/stock-api"); + + APIObject stockAPI = new APIObject("stock-api", "stock.api", "http://localhost:8080", "/"); + HttpEntity apiEntity = new HttpEntity<>(stockAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + + assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode()); + + addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + assertEquals(HttpStatus.CONFLICT, addAPIResp.getStatusCode()); + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + + assertTrue(apiListResp.contains("stock-api")); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + + assertEquals("10000", stockPriceResp.getBody()); + } + + @Test + public void givenKongAdminAPI_whenAddAPIConsumer_thenAdded() { + restTemplate.delete("http://localhost:8001/consumers/eugenp"); + + ConsumerObject consumer = new ConsumerObject("eugenp"); + HttpEntity addConsumerEntity = new HttpEntity<>(consumer); + ResponseEntity addConsumerResp = restTemplate.postForEntity("http://localhost:8001/consumers/", addConsumerEntity, String.class); + + assertEquals(HttpStatus.CREATED, addConsumerResp.getStatusCode()); + + addConsumerResp = restTemplate.postForEntity("http://localhost:8001/consumers", addConsumerEntity, String.class); + assertEquals(HttpStatus.CONFLICT, addConsumerResp.getStatusCode()); + + String consumerListResp = restTemplate.getForObject("http://localhost:8001/consumers/", String.class); + assertTrue(consumerListResp.contains("eugenp")); + } + + @Test + public void givenAPI_whenEnableAuth_thenAnonymousDenied() throws Exception { + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + if (!apiListResp.contains("stock-api")) { + givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong(); + } + + PluginObject authPlugin = new PluginObject("key-auth"); + ResponseEntity enableAuthResp = restTemplate.postForEntity("http://localhost:8001/apis/stock-api/plugins", new HttpEntity<>(authPlugin), String.class); + + assertTrue(HttpStatus.CREATED == enableAuthResp.getStatusCode() || HttpStatus.CONFLICT == enableAuthResp.getStatusCode()); + + String pluginsResp = restTemplate.getForObject("http://localhost:8001/apis/stock-api/plugins", String.class); + assertTrue(pluginsResp.contains("key-auth")); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals(HttpStatus.UNAUTHORIZED, stockPriceResp.getStatusCode()); + } + + @Test + public void givenAPIAuthEnabled_whenAddKey_thenAccessAllowed() throws Exception { + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + if (!apiListResp.contains("stock-api")) { + givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong(); + } + + String consumerListResp = restTemplate.getForObject("http://localhost:8001/consumers/", String.class); + if (!consumerListResp.contains("eugenp")) { + givenKongAdminAPI_whenAddAPIConsumer_thenAdded(); + } + + final String consumerKey = "eugenp.pass"; + KeyAuthObject keyAuth = new KeyAuthObject(consumerKey); + ResponseEntity keyAuthResp = restTemplate.postForEntity("http://localhost:8001/consumers/eugenp/key-auth", new HttpEntity<>(keyAuth), String.class); + + assertTrue(HttpStatus.CREATED == keyAuthResp.getStatusCode() || HttpStatus.CONFLICT == keyAuthResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + headers.set("apikey", consumerKey); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + + assertEquals("10000", stockPriceResp.getBody()); + + headers.set("apikey", "wrongpass"); + requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals(HttpStatus.FORBIDDEN, stockPriceResp.getStatusCode()); + } + + @Test + public void givenAdminAPIProxy_whenAddAPIViaProxy_thenAPIAdded() throws Exception { + APIObject adminAPI = new APIObject("admin-api", "admin.api", "http://localhost:8001", "/admin-api"); + HttpEntity apiEntity = new HttpEntity<>(adminAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "admin.api"); + APIObject baeldungAPI = new APIObject("baeldung-api", "baeldung.com", "http://ww.baeldung.com", "/"); + RequestEntity requestEntity = new RequestEntity<>(baeldungAPI, headers, HttpMethod.POST, new URI("http://localhost:8000/admin-api/apis")); + addAPIResp = restTemplate.exchange(requestEntity, String.class); + + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + } + +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java b/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java new file mode 100644 index 0000000000..f8090e4c86 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java @@ -0,0 +1,68 @@ +package com.baeldung.kong; + +import com.baeldung.kong.domain.APIObject; +import com.baeldung.kong.domain.TargetObject; +import com.baeldung.kong.domain.UpstreamObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + +/** + * @author aiet + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = DEFINED_PORT, classes = StockApp.class) +public class KongLoadBalanceLiveTest { + + @Before + public void init() { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + } + + @Autowired TestRestTemplate restTemplate; + + @Test + public void givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong() throws Exception { + UpstreamObject upstream = new UpstreamObject("stock.api.service"); + ResponseEntity addUpstreamResp = restTemplate.postForEntity("http://localhost:8001/upstreams", new HttpEntity<>(upstream), String.class); + assertTrue(HttpStatus.CREATED == addUpstreamResp.getStatusCode() || HttpStatus.CONFLICT == addUpstreamResp.getStatusCode()); + + TargetObject testTarget = new TargetObject("localhost:8080", 10); + ResponseEntity addTargetResp = restTemplate.postForEntity("http://localhost:8001/upstreams/stock.api.service/targets", new HttpEntity<>(testTarget), String.class); + assertTrue(HttpStatus.CREATED == addTargetResp.getStatusCode() || HttpStatus.CONFLICT == addTargetResp.getStatusCode()); + + TargetObject releaseTarget = new TargetObject("localhost:9090", 40); + addTargetResp = restTemplate.postForEntity("http://localhost:8001/upstreams/stock.api.service/targets", new HttpEntity<>(releaseTarget), String.class); + assertTrue(HttpStatus.CREATED == addTargetResp.getStatusCode() || HttpStatus.CONFLICT == addTargetResp.getStatusCode()); + + APIObject stockAPI = new APIObject("balanced-stock-api", "balanced.stock.api", "http://stock.api.service", "/"); + HttpEntity apiEntity = new HttpEntity<>(stockAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "balanced.stock.api"); + for (int i = 0; i < 1000; i++) { + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals("10000", stockPriceResp.getBody()); + } + + int releaseCount = restTemplate.getForObject("http://localhost:9090/stock/reqcount", Integer.class); + int testCount = restTemplate.getForObject("http://localhost:8080/stock/reqcount", Integer.class); + + assertTrue(Math.round(releaseCount * 1.0 / testCount) == 4); + } + +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java new file mode 100644 index 0000000000..f386712444 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java @@ -0,0 +1,54 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class APIObject { + + public APIObject() { + } + + public APIObject(String name, String hosts, String upstream_url, String uris) { + this.name = name; + this.hosts = hosts; + this.upstream_url = upstream_url; + this.uris = uris; + } + + private String name; + private String hosts; + private String upstream_url; + private String uris; + + public String getUris() { + return uris; + } + + public void setUris(String uris) { + this.uris = uris; + } + + public String getUpstream_url() { + return upstream_url; + } + + public void setUpstream_url(String upstream_url) { + this.upstream_url = upstream_url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHosts() { + return hosts; + } + + public void setHosts(String hosts) { + this.hosts = hosts; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java new file mode 100644 index 0000000000..74bef8f2d1 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java @@ -0,0 +1,35 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class ConsumerObject { + + private String username; + private String custom_id; + + public ConsumerObject(String username) { + this.username = username; + } + + public ConsumerObject(String username, String custom_id) { + this.username = username; + this.custom_id = custom_id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCustom_id() { + return custom_id; + } + + public void setCustom_id(String custom_id) { + this.custom_id = custom_id; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java new file mode 100644 index 0000000000..80de6bfcd9 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java @@ -0,0 +1,21 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class KeyAuthObject { + + public KeyAuthObject(String key) { + this.key = key; + } + + private String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java new file mode 100644 index 0000000000..c161fc9b54 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java @@ -0,0 +1,30 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class PluginObject { + + private String name; + private String consumer_id; + + public PluginObject(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getConsumer_id() { + return consumer_id; + } + + public void setConsumer_id(String consumer_id) { + this.consumer_id = consumer_id; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java new file mode 100644 index 0000000000..79653e2846 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java @@ -0,0 +1,31 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class TargetObject { + + public TargetObject(String target, int weight) { + this.target = target; + this.weight = weight; + } + + private String target; + private int weight; + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java new file mode 100644 index 0000000000..6461381ac5 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java @@ -0,0 +1,21 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class UpstreamObject { + + public UpstreamObject(String name) { + this.name = name; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java index 17e7d2d9e0..5dd6ab8e17 100644 --- a/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java +++ b/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java @@ -10,8 +10,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; 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.WiserMessage; diff --git a/spring-cloud/README.md b/spring-cloud/README.md index b24e5b0296..adaf3052e6 100644 --- a/spring-cloud/README.md +++ b/spring-cloud/README.md @@ -12,11 +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. +### Relevant Articles: - [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) - [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) - -### 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) diff --git a/spring-cloud/spring-cloud-aws/README.md b/spring-cloud/spring-cloud-aws/README.md index c5f58159c8..1340712c7a 100644 --- a/spring-cloud/spring-cloud-aws/README.md +++ b/spring-cloud/spring-cloud-aws/README.md @@ -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.password=se3retpass ``` +Multiple application classes are available under this project. To launch InstanceProfileAwsApplication application, replace `start-class` under `pom.xml`: + +``` +com.baeldung.spring.cloud.aws.InstanceProfileAwsApplication +``` \ No newline at end of file diff --git a/spring-cloud/spring-cloud-aws/pom.xml b/spring-cloud/spring-cloud-aws/pom.xml index 632e050d92..b27b6c0d18 100644 --- a/spring-cloud/spring-cloud-aws/pom.xml +++ b/spring-cloud/spring-cloud-aws/pom.xml @@ -19,6 +19,7 @@ + com.baeldung.spring.cloud.aws.SpringCloudAwsApplication UTF-8 UTF-8 1.8 diff --git a/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java new file mode 100644 index 0000000000..80c0851a6f --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java @@ -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); + } + +} diff --git a/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java new file mode 100644 index 0000000000..a0fdce2143 --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java @@ -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); + } + +} diff --git a/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml b/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml new file mode 100644 index 0000000000..f8c3d3c915 --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml @@ -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 + diff --git a/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties b/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties new file mode 100644 index 0000000000..23ca09c85c --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties @@ -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 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/README.md b/spring-cloud/spring-cloud-security/README.md new file mode 100644 index 0000000000..39af52c077 --- /dev/null +++ b/spring-cloud/spring-cloud-security/README.md @@ -0,0 +1,29 @@ +# README # + +This README would normally document whatever steps are necessary to get your application up and running. + +### What is this repository for? ### + +* Quick summary +* Version +* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) + +### How do I get set up? ### + +* Summary of set up +* Configuration +* Dependencies +* Database configuration +* How to run tests +* Deployment instructions + +### Contribution guidelines ### + +* Writing tests +* Code review +* Other guidelines + +### Who do I talk to? ### + +* Repo owner or admin +* Other community or team contact \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/alias.rtf b/spring-cloud/spring-cloud-security/alias.rtf new file mode 100644 index 0000000000..15509e1c83 --- /dev/null +++ b/spring-cloud/spring-cloud-security/alias.rtf @@ -0,0 +1,28 @@ +myauthkey + + +security: + oauth2: + resource: + jwt: + keyValue: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjj4JDMgT4OoaXisEd8Nz + uiLwum9mh8BH1l9Atpe+uZkepf3Vnv0Bhxn0BGR+kYGwEHZPVpWsHEyTfIRdinaQ + vlPaxWJquQW25yYstrCuQTKJvFjSO/cX/V4OGi1RUj76mOpwzkm1Kui3R7Sfh8Zo + WO0GiWIFJqNBsZ9b1wOfBMXnge+A+u/qxVNnTFpwCVj6k2Yb4YUsmLNCmND7E3Ra + BnrNQWqMU2numhV+ADpmVH08m/+pWdZ896uYu/tvQnz3agvZPcFsEst7LcNAWQFT + eNLkfwVfepKWa9jPELemtTLf1MkMppU+Lj1UNCr8x4Y6EupRDZhplVNtqYsPNDpO + 7wIDAQAB + -----END PUBLIC KEY----- + + + +jwt: + certificate: + store: + file: classpath:/certificate/my-auth-server.jks + password: storepassword + key: + alias: myauthserver + password: keypassword \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/pom.xml b/spring-cloud/spring-cloud-security/authserver/pom.xml new file mode 100644 index 0000000000..ed88ac046b --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + com.baeldung + authserver + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.9.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 1.1.2.RELEASE + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/AuthServer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/AuthServer.java new file mode 100644 index 0000000000..c5b330b0e3 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/AuthServer.java @@ -0,0 +1,12 @@ +package com.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.*; + +@SpringBootApplication +public class AuthServer { + + public static void main(String[] args) { + SpringApplication.run(AuthServer.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/AuthServerConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/AuthServerConfigurer.java new file mode 100644 index 0000000000..32e445f998 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/AuthServerConfigurer.java @@ -0,0 +1,77 @@ +package com.baeldung.config; + +import java.security.KeyPair; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.Resource; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; + +@Configuration +@EnableAuthorizationServer +@Order(6) +public class AuthServerConfigurer + extends + AuthorizationServerConfigurerAdapter { + + @Value("${jwt.certificate.store.file}") + private Resource keystore; + + @Value("${jwt.certificate.store.password}") + private String keystorePassword; + + @Value("${jwt.certificate.key.alias}") + private String keyAlias; + + @Value("${jwt.certificate.key.password}") + private String keyPassword; + + @Autowired + private UserDetailsService userDetailsService; + + @Override + public void configure( + ClientDetailsServiceConfigurer clients) + throws Exception { + clients + .inMemory() + .withClient("authserver") + .secret("passwordforauthserver") + .redirectUris("http://localhost:8080/") + .authorizedGrantTypes("authorization_code", + "refresh_token") + .scopes("myscope") + .autoApprove(true) + .accessTokenValiditySeconds(30) + .refreshTokenValiditySeconds(1800); + } + + @Override + public void configure( + AuthorizationServerEndpointsConfigurer endpoints) + throws Exception { + endpoints + .accessTokenConverter(jwtAccessTokenConverter()) + .userDetailsService(userDetailsService); + } + + @Bean + public JwtAccessTokenConverter jwtAccessTokenConverter() { + KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory( + keystore, keystorePassword.toCharArray()); + KeyPair keyPair = keyStoreKeyFactory.getKeyPair( + keyAlias, keyPassword.toCharArray()); + JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); + converter.setKeyPair(keyPair); + return converter; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/ResourceServerConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/ResourceServerConfigurer.java new file mode 100644 index 0000000000..f97544dc59 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/ResourceServerConfigurer.java @@ -0,0 +1,21 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; + +/** + * Our configuration for the OAuth2 User Info Resource Server. + */ +@Configuration +@EnableResourceServer +public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter { + @Override + public void configure(HttpSecurity http) throws Exception { + http.antMatcher("/user") + .authorizeRequests() + .anyRequest() + .authenticated(); + } +} diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebMvcConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebMvcConfigurer.java new file mode 100644 index 0000000000..23b56151e7 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebMvcConfigurer.java @@ -0,0 +1,14 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class WebMvcConfigurer extends WebMvcConfigurerAdapter { + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("login").setViewName("login"); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebSecurityConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebSecurityConfigurer.java new file mode 100644 index 0000000000..44406b8fa0 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/config/WebSecurityConfigurer.java @@ -0,0 +1,52 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; + +@Configuration +@EnableWebSecurity +@EnableOAuth2Client +public class WebSecurityConfigurer + extends + WebSecurityConfigurerAdapter { + + + @Override + protected void configure(HttpSecurity http) + throws Exception { + http + .authorizeRequests() + .antMatchers("/login**").permitAll() + .anyRequest().authenticated() + .and().csrf() + .and().formLogin().loginPage("/login"); + } + + @Override + protected void configure( + AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("user") + .roles("USER") + .and() + .withUser("admin").password("admin") + .roles("USER", "ADMIN"); + } + + @Override + @Bean(name = "userDetailsService") + public UserDetailsService userDetailsServiceBean() + throws Exception { + return super.userDetailsServiceBean(); + } + + +} diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/controller/ResourceController.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/controller/ResourceController.java new file mode 100644 index 0000000000..739a6f6240 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/baeldung/controller/ResourceController.java @@ -0,0 +1,20 @@ +package com.baeldung.controller; + +import java.security.Principal; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Because this application is also a User Info Resource Server, we expose info about the logged in user at: + * + * http://localhost:9090/auth/user + */ +@RestController +public class ResourceController { + + @RequestMapping("/user") + public Principal user(Principal user) { + return user; + } + +} diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/authserver/src/main/resources/application.yml new file mode 100644 index 0000000000..1dc63d3f0e --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/resources/application.yml @@ -0,0 +1,21 @@ +# Make the application available at http://localhost:7070/authserver +server: + port: 7070 + contextPath: /authserver + +# Our certificate settings for enabling JWT tokens +jwt: + certificate: + store: + file: classpath:/certificate/mykeystore.jks + password: abirkhan04 + key: + alias: myauthkey + password: abirkhan04 + + +security: + oauth2: + resource: + filter-order: 3 + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks b/spring-cloud/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks new file mode 100644 index 0000000000..9cf25e3224 Binary files /dev/null and b/spring-cloud/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks differ diff --git a/spring-cloud/spring-cloud-security/authserver/src/main/resources/templates/login.html b/spring-cloud/spring-cloud-security/authserver/src/main/resources/templates/login.html new file mode 100644 index 0000000000..f5ab5a6f26 --- /dev/null +++ b/spring-cloud/spring-cloud-security/authserver/src/main/resources/templates/login.html @@ -0,0 +1,29 @@ + + + + + Baeldung Spring cloud Security + + + +

Login

+ + +
+
+

Username and Password:

+

+ + +

+

+ + +

+

+ +

+
+
+ + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/mykeystore.jks b/spring-cloud/spring-cloud-security/mykeystore.jks new file mode 100644 index 0000000000..9cf25e3224 Binary files /dev/null and b/spring-cloud/spring-cloud-security/mykeystore.jks differ diff --git a/spring-cloud/spring-cloud-security/oauth2client/pom.xml b/spring-cloud/spring-cloud-security/oauth2client/pom.xml new file mode 100644 index 0000000000..fd1c6964e1 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + com.baeldung + oauth2client + 0.0.1-SNAPSHOT + jar + + oauth2client + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.9.RELEASE + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Dalston.SR4 + pom + import + + + + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-oauth2 + + + org.springframework.cloud + spring-cloud-starter-zuul + + + org.springframework.boot + spring-boot-starter-test + test + + + org.webjars + jquery + + + org.webjars + bootstrap + + + org.webjars + webjars-locator + + + + org.springframework.boot + spring-boot-starter-security + + + org.webjars + js-cookie + 2.1.0 + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/CloudSite.java b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/CloudSite.java new file mode 100644 index 0000000000..a072605ec0 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/CloudSite.java @@ -0,0 +1,22 @@ +package com.baeldung; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import com.baeldung.filters.SimpleFilter; + + +@SpringBootApplication +public class CloudSite { + public static void main(String[] args) { + SpringApplication.run(CloudSite.class, args); + } + + @Bean + public SimpleFilter simpleFilter() { + return new SimpleFilter(); + } + +} diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/config/SiteSecurityConfigurer.java b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/config/SiteSecurityConfigurer.java new file mode 100644 index 0000000000..217edb22fb --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/config/SiteSecurityConfigurer.java @@ -0,0 +1,49 @@ +package com.baeldung.config; + +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestOperations; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; + +@EnableZuulProxy +@Configuration +@EnableOAuth2Sso +public class SiteSecurityConfigurer + extends + WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) + throws Exception { + http.antMatcher("/**") + .authorizeRequests() + .antMatchers("/", "/webjars/**") + .permitAll() + .anyRequest() + .authenticated() + .and() + .logout() + .logoutSuccessUrl("/") + .permitAll() + .and() + .csrf() + .csrfTokenRepository( + CookieCsrfTokenRepository + .withHttpOnlyFalse()); + } + + @Bean + public OAuth2RestOperations restOperations( + OAuth2ProtectedResourceDetails resource, + OAuth2ClientContext context) { + return new OAuth2RestTemplate(resource, context); + } + +} diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/controller/CloudSiteController.java b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/controller/CloudSiteController.java new file mode 100644 index 0000000000..b6bfd0bcf6 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/controller/CloudSiteController.java @@ -0,0 +1,30 @@ +package com.baeldung.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestOperations; +import org.springframework.web.servlet.ModelAndView; + +@RestController +public class CloudSiteController { + + @Autowired + private RestOperations restOperations; + + @GetMapping("/") + @ResponseBody + public String helloFromBaeldung() { + return "Hello From Baeldung!"; + } + + @GetMapping("/person") + public ModelAndView person() { + ModelAndView mav = new ModelAndView("personinfo"); + String personResourceUrl = "http://localhost:9000/personResource"; + mav.addObject("person", restOperations.getForObject(personResourceUrl, String.class)); + return mav; + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/filters/SimpleFilter.java b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/filters/SimpleFilter.java new file mode 100644 index 0000000000..98e25ac9c4 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/java/com/baeldung/filters/SimpleFilter.java @@ -0,0 +1,39 @@ +package com.baeldung.filters; + +import javax.servlet.http.HttpServletRequest; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.ZuulFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimpleFilter extends ZuulFilter { + + private static Logger log = LoggerFactory.getLogger(SimpleFilter.class); + + @Override + public String filterType() { + return "pre"; + } + + @Override + public int filterOrder() { + return 1; + } + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + RequestContext ctx = RequestContext.getCurrentContext(); + HttpServletRequest request = ctx.getRequest(); + + log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString())); + + return null; + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/application.properties b/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/application.yml new file mode 100644 index 0000000000..06a950d270 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/application.yml @@ -0,0 +1,37 @@ +# Make the application available at http://localhost:8080 +# These are default settings, but we add them for clarity. +server: + port: 8080 + contextPath: / + +# Configure the Authorization Server and User Info Resource Server details +security: + oauth2: + client: + accessTokenUri: http://localhost:7070/authserver/oauth/token + userAuthorizationUri: http://localhost:7070/authserver/oauth/authorize + clientId: authserver + clientSecret: passwordforauthserver + resource: + userInfoUri: http://localhost:7070/authserver/user + +person: + url: http://localhost:9000/person + +# Proxies the calls to http://localhost:8080/api/* to our REST service at http://localhost:8081/* +# and automatically includes our OAuth2 token in the request headers +zuul: + routes: + resource: + path: /api/** + url: http://localhost:9000 + user: + path: /user/** + url: http://localhost:7070/authserver/user + +# Make sure the OAuth2 token is only relayed when using the internal API, +# do not pass any authentication to the external API +proxy: + auth: + routes: + api: oauth2 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/templates/personinfo.html b/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/templates/personinfo.html new file mode 100644 index 0000000000..bc3197e93a --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/main/resources/templates/personinfo.html @@ -0,0 +1,13 @@ + + + + +Getting Personal Information + + +

Providing Person Information

+

+ Person's information: +

+ + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/oauth2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java b/spring-cloud/spring-cloud-security/oauth2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java new file mode 100644 index 0000000000..5fa51a61c3 --- /dev/null +++ b/spring-cloud/spring-cloud-security/oauth2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java @@ -0,0 +1,16 @@ +package com.example.springoath2; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class Springoath2ApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/spring-cloud/spring-cloud-security/personresource/pom.xml b/spring-cloud/spring-cloud-security/personresource/pom.xml new file mode 100644 index 0000000000..ca1ff82515 --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + com.baeldung + personresource + 0.0.1-SNAPSHOT + jar + + personresource + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.9.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Edgware.RELEASE + + + + + org.springframework.security.oauth + spring-security-oauth2 + + + org.springframework.cloud + spring-cloud-starter-security + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-jwt + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/Application.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/Application.java new file mode 100644 index 0000000000..8c087476bf --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/Application.java @@ -0,0 +1,12 @@ +package com.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/config/ResourceConfigurer.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/config/ResourceConfigurer.java new file mode 100644 index 0000000000..977d74093a --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/config/ResourceConfigurer.java @@ -0,0 +1,25 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; + +/** + * REST API Resource Server. + */ +@Configuration +@EnableWebSecurity +@EnableResourceServer +@EnableGlobalMethodSecurity(prePostEnabled = true) // Allow method annotations like @PreAuthorize +public class ResourceConfigurer extends ResourceServerConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + http.httpBasic().disable(); + http.authorizeRequests().anyRequest().authenticated(); + } + +} diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java new file mode 100644 index 0000000000..9e5420da5a --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java @@ -0,0 +1,18 @@ +package com.baeldung.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.model.Person; + +@RestController +public class PersonInfoController { + + @GetMapping("/personResource") + @PreAuthorize("hasAnyRole('ADMIN', 'USER')") + public @ResponseBody Person personInfo() { + return new Person("abir", "Dhaka", "Bangladesh", 29, "Male"); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/model/Person.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/model/Person.java new file mode 100644 index 0000000000..b568291da3 --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/model/Person.java @@ -0,0 +1,59 @@ +package com.baeldung.model; + +public class Person { + + private String name; + private String city; + private String country; + private Integer age; + private String sex; + + public Person(String name, String city, String country, Integer age, String sex) { + this.name = name; + this.city = city; + this.country = country; + this.age = age; + this.sex = sex; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/personresource/src/main/resources/application.yml new file mode 100644 index 0000000000..20a3313a60 --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/main/resources/application.yml @@ -0,0 +1,22 @@ +# Make the application available at http://localhost:9000 + +server: + port: 9000 + +# Configure the public key to use for verifying the incoming JWT tokens +security: + sessions: NEVER + oauth2: + resource: + userInfoUri: http://localhost:7070/authserver/user + jwt: + keyValue: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhiiifKv6Otf5PyqIE+LQ + EiJRRh6q8piPY9Okq+RfRu9Bue0D8hq7aFxcgkLZ6Bg9CAS+w1KdaE5MMeOCVVxv + rpRETzVpAsh6GL5nBc679jSqMzjr3V4uty46ilL4VHKSxlZh5Nmz5EMHPI5iwpNs + 8U5n3QiwsTk514FXad54xPSPH3i/pDzGSZHrVcwDVaOKn7gFiIqP86vkJB47JZv8 + T6P5RK7Rj06zoG45DMGWG3DQv6o1/Jm4IJQWj0AUD3bSHqzXkPr7qyMYvkE4kyMH + 6aVAsAYMxilZFlJMv2b8N883gdi3LEeOJo8zZr5IWyyROfepdeOL7UkAXddAj+dL + WQIDAQAB + -----END PUBLIC KEY----- \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/personresource/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java b/spring-cloud/spring-cloud-security/personresource/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java new file mode 100644 index 0000000000..e0fe7006d9 --- /dev/null +++ b/spring-cloud/spring-cloud-security/personresource/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java @@ -0,0 +1,16 @@ +package com.baeldung.service.personservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PersonserviceApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/spring-cloud/spring-cloud-security/pubkey.txt b/spring-cloud/spring-cloud-security/pubkey.txt new file mode 100644 index 0000000000..2c391ba2dd --- /dev/null +++ b/spring-cloud/spring-cloud-security/pubkey.txt @@ -0,0 +1,30 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhiiifKv6Otf5PyqIE+LQ +EiJRRh6q8piPY9Okq+RfRu9Bue0D8hq7aFxcgkLZ6Bg9CAS+w1KdaE5MMeOCVVxv +rpRETzVpAsh6GL5nBc679jSqMzjr3V4uty46ilL4VHKSxlZh5Nmz5EMHPI5iwpNs +8U5n3QiwsTk514FXad54xPSPH3i/pDzGSZHrVcwDVaOKn7gFiIqP86vkJB47JZv8 +T6P5RK7Rj06zoG45DMGWG3DQv6o1/Jm4IJQWj0AUD3bSHqzXkPr7qyMYvkE4kyMH +6aVAsAYMxilZFlJMv2b8N883gdi3LEeOJo8zZr5IWyyROfepdeOL7UkAXddAj+dL +WQIDAQAB +-----END PUBLIC KEY----- +-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIEDqsC7jANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwI4 +ODETMBEGA1UECBMKQmFuZ2xhZGVzaDEOMAwGA1UEBxMFRGhha2ExETAPBgNVBAoT +CEJhZWxkdW5nMRUwEwYDVQQLEwxCYWVsZHVuZ2Jsb2cxEjAQBgNVBAMTCWxvY2Fs +aG9zdDAeFw0xNzEyMjUxNDE0MDhaFw0xODAzMjUxNDE0MDhaMHAxCzAJBgNVBAYT +Ajg4MRMwEQYDVQQIEwpCYW5nbGFkZXNoMQ4wDAYDVQQHEwVEaGFrYTERMA8GA1UE +ChMIQmFlbGR1bmcxFTATBgNVBAsTDEJhZWxkdW5nYmxvZzESMBAGA1UEAxMJbG9j +YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhiiifKv6Otf5 +PyqIE+LQEiJRRh6q8piPY9Okq+RfRu9Bue0D8hq7aFxcgkLZ6Bg9CAS+w1KdaE5M +MeOCVVxvrpRETzVpAsh6GL5nBc679jSqMzjr3V4uty46ilL4VHKSxlZh5Nmz5EMH +PI5iwpNs8U5n3QiwsTk514FXad54xPSPH3i/pDzGSZHrVcwDVaOKn7gFiIqP86vk +JB47JZv8T6P5RK7Rj06zoG45DMGWG3DQv6o1/Jm4IJQWj0AUD3bSHqzXkPr7qyMY +vkE4kyMH6aVAsAYMxilZFlJMv2b8N883gdi3LEeOJo8zZr5IWyyROfepdeOL7UkA +XddAj+dLWQIDAQABoyEwHzAdBgNVHQ4EFgQUHLFYkq36Wami5qsVRe/1eQedmdgw +DQYJKoZIhvcNAQELBQADggEBABL3lYyuRd6Hv8DPus/zQL0bRl6gVsEzczwmWMUA +3NJZbUHAD/KC732aArvKIKykkbLG6K/Mhnfuu8YBfWzTvGgY3Ww+ka2sJFOsUW7r +sa6OBtNHh4zhDYN2Weza+4jnRLxtkzFbm6v2sheFkyB1NywCwFE/6p1Z6KTG8RyJ +gw/OHl6rb+Y/T6cOeeTCFUN/v+qRVSB9I/MjSK5wRNbFT+MyNUeL6gsiyIvxSZbj +y4vrjGHkXasSmwkfvgw67mJMk4XTGrVLjIXUTyzbdSmodcv8N6nrsIk4SBYCnTrI +E/5NtNgbOFGwovde5yNrZIjjAC1VGOmVFhcxFJpwT6ZkSks= +-----END CERTIFICATE----- diff --git a/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh new file mode 100644 index 0000000000..ca8298430b --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh @@ -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 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/.gitignore b/spring-cloud/spring-cloud-stream-starters/boot/.gitignore new file mode 100644 index 0000000000..e4b82e1c0f --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/.gitignore @@ -0,0 +1,3 @@ +.idea +*/target/* +*.iml diff --git a/spring-cloud/spring-cloud-stream-starters/boot/pom.xml b/spring-cloud/spring-cloud-stream-starters/boot/pom.xml new file mode 100644 index 0000000000..3e6bc134e3 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + com.baeldung.twitterhdfs + twitterhdfs + jar + 1.0.0 + + twitterhdfs + + + UTF-8 + UTF-8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.RELEASE + + + + + + org.springframework.cloud.stream.app + spring-cloud-starter-stream-source-twitterstream + 1.3.1.RELEASE + + + org.springframework.cloud.stream.app + spring-cloud-starter-stream-sink-hdfs + 1.3.1.RELEASE + + + + + javax.servlet + jstl + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + twitterhdfs + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java new file mode 100644 index 0000000000..8b9ca6dc62 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java @@ -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); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java new file mode 100644 index 0000000000..e3bd1197f6 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java @@ -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; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java new file mode 100644 index 0000000000..c0c1e287d3 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java @@ -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); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java new file mode 100644 index 0000000000..f9b220561b --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java @@ -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()); + } + +} diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties b/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties new file mode 100644 index 0000000000..298a8ebf4d --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties @@ -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= \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties new file mode 100644 index 0000000000..1f4aaf88dd --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties @@ -0,0 +1 @@ +hdfs.fs-uri=hdfs://127.0.0.1:50010/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh new file mode 100644 index 0000000000..577a25dd6e --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -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 apps +# Optionally inject application.properties prior to build +java -jar hdfs-sink.jar --fsUri=hdfs://127.0.0.1:50010/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/twitter/application.properties b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties new file mode 100644 index 0000000000..e38612d25e --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties @@ -0,0 +1,4 @@ +twitter.credentials.access-token= +twitter.credentials.access-token-secret= +twitter.credentials.consumer-key= +twitter.credentials.consumer-secret= \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh new file mode 100644 index 0000000000..4c76fe637b --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -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 apps +# Optionally inject application.properties prior to build +java -jar twitter_stream_source.jar --consumerKey= --consumerSecret= \ + --accessToken= --accessTokenSecret= \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/java/com/baeldung/spring/cloud/eureka/client/EurekaClientApplication.java b/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/java/com/baeldung/spring/cloud/eureka/client/EurekaClientApplication.java index 906d6e4cfd..9bbb121408 100644 --- a/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/java/com/baeldung/spring/cloud/eureka/client/EurekaClientApplication.java +++ b/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/java/com/baeldung/spring/cloud/eureka/client/EurekaClientApplication.java @@ -21,12 +21,17 @@ public class EurekaClientApplication implements GreetingController { @Value("${spring.application.name}") private String appName; + @Value("${server.port}") + private String portNumber; + public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } @Override public String greeting() { - return String.format("Hello from '%s'!", eurekaClient.getApplication(appName).getName()); + System.out.println("Request received on port number " + portNumber); + return String.format("Hello from '%s with Port Number %s'!", eurekaClient.getApplication(appName) + .getName(), portNumber); } } diff --git a/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/resources/application.yml b/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/resources/application.yml index 08624aa159..9fa929e16b 100644 --- a/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/resources/application.yml +++ b/spring-cloud/spring-cloud-zuul-eureka-integration/eureka-client/src/main/resources/application.yml @@ -3,7 +3,7 @@ spring: name: spring-cloud-eureka-client server: - port: 0 + port: 8081 eureka: client: diff --git a/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java b/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java index b8ad59e2e2..09b971fdc2 100644 --- a/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java +++ b/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java @@ -11,10 +11,9 @@ public class Person { private Date dateOfBirth; public Person() { - } - public Person(int age, String fullName, Date dateOfBirth) { + Person(int age, String fullName, Date dateOfBirth) { super(); this.age = age; this.fullName = fullName; diff --git a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/repository/ArticleRepository.java b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/repository/ArticleRepository.java index 8aef865401..93812a3cea 100644 --- a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/repository/ArticleRepository.java +++ b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/repository/ArticleRepository.java @@ -1,12 +1,13 @@ 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.Pageable; import org.springframework.data.elasticsearch.annotations.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; +import com.baeldung.spring.data.es.model.Article; + @Repository public interface ArticleRepository extends ElasticsearchRepository { @@ -14,4 +15,10 @@ public interface ArticleRepository extends ElasticsearchRepository findByAuthorsNameUsingCustomQuery(String name, Pageable pageable); + + @Query("{\"bool\": {\"must\": {\"match_all\": {}}, \"filter\": {\"term\": {\"tags\": \"?0\" }}}}") + Page
findByFilteredTagQuery(String tag, Pageable pageable); + + @Query("{\"bool\": {\"must\": {\"match\": {\"authors.name\": \"?0\"}}, \"filter\": {\"term\": {\"tags\": \"?1\" }}}}") + Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable); } diff --git a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java index b5a8fde633..63e2d91fa7 100644 --- a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java +++ b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java @@ -1,9 +1,10 @@ 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.Pageable; +import com.baeldung.spring.data.es.model.Article; + public interface ArticleService { Article save(Article article); @@ -15,6 +16,10 @@ public interface ArticleService { Page
findByAuthorNameUsingCustomQuery(String name, Pageable pageable); + Page
findByFilteredTagQuery(String tag, Pageable pageable); + + Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable); + long count(); void delete(Article article); diff --git a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java index 2a31b52602..0908ffa70e 100644 --- a/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java +++ b/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java @@ -1,12 +1,13 @@ 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.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.repository.ArticleRepository; + @Service public class ArticleServiceImpl implements ArticleService { @@ -42,6 +43,16 @@ public class ArticleServiceImpl implements ArticleService { return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable); } + @Override + public Page
findByFilteredTagQuery(String tag, Pageable pageable) { + return articleRepository.findByFilteredTagQuery(tag, pageable); + } + + @Override + public Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable) { + return articleRepository.findByAuthorsNameAndFilteredTagQuery(name, tag, pageable); + } + @Override public long count() { return articleRepository.count(); diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java index 1fb1ae76f7..285c164869 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import static org.elasticsearch.node.NodeBuilder.nodeBuilder; import static org.junit.Assert.assertEquals; @@ -78,12 +79,9 @@ public class ElasticSearchManualTest { SearchHit[] searchHits = response .getHits() .getHits(); - List results = new ArrayList<>(); - for (SearchHit hit : searchHits) { - String sourceAsString = hit.getSourceAsString(); - Person person = JSON.parseObject(sourceAsString, Person.class); - results.add(person); - } + List results = Arrays.stream(searchHits) + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); } @Test @@ -125,11 +123,10 @@ public class ElasticSearchManualTest { .actionGet(); response2.getHits(); response3.getHits(); - List searchHits = Arrays.asList(response - .getHits() - .getHits()); - final List results = new ArrayList<>(); - searchHits.forEach(hit -> results.add(JSON.parseObject(hit.getSourceAsString(), Person.class))); + + final List results = Arrays.stream(response.getHits().getHits()) + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); } @Test diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java index 19514ce4c2..aa20913637 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java @@ -1,11 +1,6 @@ package com.baeldung.elasticsearch; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - +import com.baeldung.spring.data.es.config.Config; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; @@ -15,6 +10,7 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -24,14 +20,19 @@ import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertTrue; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class GeoQueriesTest { - public static final String WONDERS_OF_WORLD = "wonders-of-world"; - public static final String WONDERS = "Wonders"; + private static final String WONDERS_OF_WORLD = "wonders-of-world"; + private static final String WONDERS = "Wonders"; + @Autowired private ElasticsearchTemplate elasticsearchTemplate; @@ -44,39 +45,37 @@ public class GeoQueriesTest { CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); req.mapping(WONDERS, jsonObject); client.admin() - .indices() - .create(req) - .actionGet(); + .indices() + .create(req) + .actionGet(); } @Test public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,25],[80.1,30.2]]}}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String tajMahalId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoShapeQuery("region", ShapeBuilder.newEnvelope() - .topLeft(74.00, 24.0) - .bottomRight(81.1, 31.2)) - .relation(ShapeRelation.WITHIN); + .topLeft(74.00, 24.0) + .bottomRight(81.1, 31.2)) + .relation(ShapeRelation.WITHIN); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(tajMahalId)); } @@ -84,29 +83,27 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String pyramidsOfGizaId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location") - .bottomLeft(28, 30) - .topRight(31, 32); + .bottomLeft(28, 30) + .topRight(31, 32); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(pyramidsOfGizaId)); } @@ -114,29 +111,27 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String lighthouseOfAlexandriaId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") - .point(29.976, 31.131) - .distance(10, DistanceUnit.MILES); + .point(29.976, 31.131) + .distance(10, DistanceUnit.MILES); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(lighthouseOfAlexandriaId)); } @@ -144,30 +139,28 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String greatRannOfKutchid = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoPolygonQuery("location") - .addPoint(22.733, 68.859) - .addPoint(24.733, 68.859) - .addPoint(23, 70.859); + .addPoint(22.733, 68.859) + .addPoint(24.733, 68.859) + .addPoint(23, 70.859); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(greatRannOfKutchid)); } @@ -175,5 +168,4 @@ public class GeoQueriesTest { public void destroy() { elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD); } - } diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java index 1280c8e1de..41965fbb83 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java @@ -1,15 +1,9 @@ package com.baeldung.spring.data.es; -import static java.util.Arrays.asList; -import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; -import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.List; - +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.service.ArticleService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -22,10 +16,15 @@ import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; +import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) @@ -47,14 +46,22 @@ public class ElasticSearchIntegrationTest { Article article = new Article("Spring Data Elasticsearch"); article.setAuthors(asList(johnSmith, johnDoe)); + article.setTags("elasticsearch", "spring data"); articleService.save(article); article = new Article("Search engines"); article.setAuthors(asList(johnDoe)); + article.setTags("search engines", "tutorial"); articleService.save(article); article = new Article("Second Article About Elasticsearch"); 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); } @@ -72,21 +79,34 @@ public class ElasticSearchIntegrationTest { @Test public void givenPersistedArticles_whenSearchByAuthorsName_thenRightFound() { - final Page
articleByAuthorName = articleService.findByAuthorName(johnSmith.getName(), new PageRequest(0, 10)); + final Page
articleByAuthorName = articleService + .findByAuthorName(johnSmith.getName(), new PageRequest(0, 10)); assertEquals(2L, articleByAuthorName.getTotalElements()); } @Test public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() { + final Page
articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("Smith", new PageRequest(0, 10)); + assertEquals(2L, articleByAuthorName.getTotalElements()); + } - final Page
articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10)); + @Test + public void givenTagFilterQuery_whenSearchByTag_thenArticleIsFound() { + final Page
articleByAuthorName = articleService.findByFilteredTagQuery("elasticsearch", new PageRequest(0, 10)); assertEquals(3L, articleByAuthorName.getTotalElements()); } + @Test + public void givenTagFilterQuery_whenSearchByAuthorsName_thenArticleIsFound() { + final Page
articleByAuthorName = articleService.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", new PageRequest(0, 10)); + assertEquals(2L, articleByAuthorName.getTotalElements()); + } + @Test public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")) + .build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); @@ -112,7 +132,8 @@ public class ElasticSearchIntegrationTest { final String articleTitle = "Spring Data Elasticsearch"; - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); final long count = articleService.count(); @@ -124,7 +145,8 @@ public class ElasticSearchIntegrationTest { @Test public void givenSavedDoc_whenOneTermMatches_thenFindByTitle() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java index cc4bce0c75..c6af93bb62 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java @@ -1,20 +1,9 @@ package com.baeldung.spring.data.es; -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toList; -import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; -import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; -import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.junit.Assert.assertEquals; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.service.ArticleService; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.unit.Fuzziness; @@ -22,6 +11,7 @@ import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; @@ -35,10 +25,20 @@ import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; +import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) @@ -86,14 +86,16 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenFullTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } @Test public void givenOneTermFromTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Engines Solutions")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Engines Solutions")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); assertEquals("Search engines", articles.get(0).getTitle()); @@ -101,18 +103,21 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenPartTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "elasticsearch data")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "elasticsearch data")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(3, articles.size()); } @Test public void givenFullTitle_whenRunMatchQueryOnVerbatimField_thenDocIsFound() { - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build(); + SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build(); List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); - searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")).build(); + searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")) + .build(); articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(0, articles.size()); } @@ -130,38 +135,48 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() { final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("title"); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation).execute().actionGet(); + final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) + .execute().actionGet(); final Map results = response.getAggregations().asMap(); final StringTerms topTags = (StringTerms) results.get("top_tags"); - final List keys = topTags.getBuckets().stream().map(b -> b.getKeyAsString()).collect(toList()); - Collections.sort(keys); + final List keys = topTags.getBuckets().stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .sorted() + .collect(toList()); assertEquals(asList("about", "article", "data", "elasticsearch", "engines", "search", "second", "spring", "tutorial"), keys); } @Test public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() { - final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags").order(Terms.Order.aggregation("_count", false)); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation).execute().actionGet(); + final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags") + .order(Terms.Order.aggregation("_count", false)); + final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) + .execute().actionGet(); final Map results = response.getAggregations().asMap(); final StringTerms topTags = (StringTerms) results.get("top_tags"); - final List keys = topTags.getBuckets().stream().map(b -> b.getKeyAsString()).collect(toList()); + final List keys = topTags.getBuckets().stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .collect(toList()); assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys); } @Test public void givenNotExactPhrase_whenUseSlop_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } @Test public void givenPhraseWithType_whenUseFuzziness_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE).prefixLength(3)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE) + .prefixLength(3)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); @@ -169,9 +184,23 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenMultimatchQuery_whenDoSearch_thenAllProvidedFieldsMatch() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery("tutorial").field("title").field("tags").type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(multiMatchQuery("tutorial").field("title").field("tags") + .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); 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
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); + + assertEquals(2, articles.size()); + } } diff --git a/spring-ejb/README.md b/spring-ejb/README.md new file mode 100644 index 0000000000..8fa8833f3a --- /dev/null +++ b/spring-ejb/README.md @@ -0,0 +1,3 @@ +### Relevant Articles + +- [Integration Guide for Spring and EJB](http://www.baeldung.com/spring-ejb) diff --git a/spring-mvc-push/.gitignore b/spring-mvc-push/.gitignore deleted file mode 100644 index 448298d47f..0000000000 --- a/spring-mvc-push/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.tern-project diff --git a/spring-mvc-push/pom.xml b/spring-mvc-push/pom.xml deleted file mode 100644 index 2eb10381be..0000000000 --- a/spring-mvc-push/pom.xml +++ /dev/null @@ -1,91 +0,0 @@ - - 4.0.0 - com.baeldung - spring-mvc-push - war - 0.0.1-SNAPSHOT - spring-mvc-push - - 1.8 - 1.8 - 2.20 - 3.7.0 - 3.2.0 - UTF-8 - 5.0.2 - 5.0.2.RELEASE - 4.0.0 - 1.2 - 2.3.2-b02 - 5.0.2 - 1.0.2 - - - - org.springframework - spring-webmvc - ${spring.version} - - - javax.servlet - javax.servlet-api - ${servlet.version} - provided - - - javax.servlet - jstl - ${jstl.version} - - - javax.servlet.jsp - javax.servlet.jsp-api - ${jsp-api.version} - - - - org.springframework - spring-test - ${spring.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven.compiler.version} - - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - - spring-mvc-push - false - ${deploy-path} - - - - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.junit.platform - junit-platform-surefire-provider - ${junit.platform.version} - - - - - spring-mvc-push - - diff --git a/spring-mvc-push/src/main/java/com/baeldung/config/PushConfiguration.java b/spring-mvc-push/src/main/java/com/baeldung/config/PushConfiguration.java deleted file mode 100644 index e6188da92d..0000000000 --- a/spring-mvc-push/src/main/java/com/baeldung/config/PushConfiguration.java +++ /dev/null @@ -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/"); - } - -} diff --git a/spring-mvc-push/src/main/webapp/index.jsp b/spring-mvc-push/src/main/webapp/index.jsp deleted file mode 100644 index 82ecb68003..0000000000 --- a/spring-mvc-push/src/main/webapp/index.jsp +++ /dev/null @@ -1,14 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> - - - -PushBuilder demo - - -

- Go to PushBuilder demo
Go to - Simple demo -

- - \ No newline at end of file diff --git a/spring-mvc-push/src/main/webapp/resources/script.js b/spring-mvc-push/src/main/webapp/resources/script.js deleted file mode 100644 index 9bc97c006a..0000000000 --- a/spring-mvc-push/src/main/webapp/resources/script.js +++ /dev/null @@ -1 +0,0 @@ -console.log('Script') \ No newline at end of file diff --git a/spring-mvc-push/src/main/webapp/resources/style.css b/spring-mvc-push/src/main/webapp/resources/style.css deleted file mode 100644 index d5fc158135..0000000000 --- a/spring-mvc-push/src/main/webapp/resources/style.css +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/spring-mvc-simple/README.md b/spring-mvc-simple/README.md index 197a22cbac..69a9027280 100644 --- a/spring-mvc-simple/README.md +++ b/spring-mvc-simple/README.md @@ -2,3 +2,4 @@ - [HandlerAdapters in Spring MVC](http://www.baeldung.com/spring-mvc-handler-adapters) - [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) diff --git a/spring-mvc-simple/pom.xml b/spring-mvc-simple/pom.xml index 2cc6aec906..595e58f5f3 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -8,83 +8,63 @@ Spring MVC simple Maven Webapp http://maven.apache.org - - com.baeldung - parent-modules - 1.0.0-SNAPSHOT - - - 4.3.10.RELEASE - 3.1.0 + 1.8 + 1.8 + UTF-8 + 5.0.2.RELEASE + 3.2.0 + 3.7.0 + 2.20 1.2 - 2.3.1 - 3.1.0 + 2.3.2-b02 + 4.0.0 5.4.1.Final enter-location-of-server 1.3.2 1.8 3.0.7.RELEASE 2.4.12 - 2.3.23 + 2.3.27-incubating 1.2.5 + 5.0.2 + 5.0.2 + 1.0.2 + 1.9.0 javax.servlet javax.servlet-api - 3.1.0 - - - org.springframework - spring-webmvc - ${springframework.version} - - - org.springframework - spring-context - ${springframework.version} - - - org.springframework - spring-core - ${springframework.version} - - - commons-logging - commons-logging - - + ${javax.servlet-api.version} javax.servlet.jsp javax.servlet.jsp-api ${javax.servlet.jsp-api.version} - javax.servlet jstl ${jstl.version} - org.hibernate hibernate-validator ${hibernate-validator.version} - - org.springframework - spring-webmvc - ${springframework.version} - commons-fileupload commons-fileupload ${fileupload.version} - + + org.springframework + spring-webmvc + ${springframework.version} + + org.thymeleaf @@ -115,7 +95,7 @@ groovy-templates ${groovy.version} - + de.neuland-bfi @@ -123,6 +103,24 @@ ${jade.version} + + + org.springframework + spring-test + ${springframework.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + com.rometools + rome + ${rome.version} + @@ -141,11 +139,18 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 - - 1.8 - 1.8 - + ${maven.compiler.version} + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java index b62ccae465..b9a8336bf2 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java @@ -7,13 +7,13 @@ import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.baeldung.springmvcforms", "com.baeldung.spring.controller", "com.baeldung.spring.validator" }) -class ApplicationConfiguration extends WebMvcConfigurerAdapter { +class ApplicationConfiguration implements WebMvcConfigurer { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/JadeTemplateConfiguration.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/JadeTemplateConfiguration.java index 10345bac58..4c95e5a0bd 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/JadeTemplateConfiguration.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/JadeTemplateConfiguration.java @@ -16,24 +16,24 @@ import de.neuland.jade4j.spring.view.JadeViewResolver; public class JadeTemplateConfiguration { @Bean public SpringTemplateLoader templateLoader() { - SpringTemplateLoader templateLoader = new SpringTemplateLoader(); - templateLoader.setBasePath("/WEB-INF/views/"); - templateLoader.setSuffix(".jade"); - return templateLoader; + SpringTemplateLoader templateLoader = new SpringTemplateLoader(); + templateLoader.setBasePath("/WEB-INF/views/"); + templateLoader.setSuffix(".jade"); + return templateLoader; } @Bean public JadeConfiguration jadeConfiguration() { - JadeConfiguration configuration = new JadeConfiguration(); - configuration.setCaching(false); - configuration.setTemplateLoader(templateLoader()); - return configuration; + JadeConfiguration configuration = new JadeConfiguration(); + configuration.setCaching(false); + configuration.setTemplateLoader(templateLoader()); + return configuration; } @Bean public ViewResolver viewResolver() { - JadeViewResolver viewResolver = new JadeViewResolver(); - viewResolver.setConfiguration(jadeConfiguration()); - return viewResolver; + JadeViewResolver viewResolver = new JadeViewResolver(); + viewResolver.setConfiguration(jadeConfiguration()); + return viewResolver; } } diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/PushConfiguration.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/PushConfiguration.java new file mode 100644 index 0000000000..3072501cfa --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/PushConfiguration.java @@ -0,0 +1,30 @@ +package com.baeldung.spring.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +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.spring.controller.push") +public class PushConfiguration implements WebMvcConfigurer { + + @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/"); + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java index d57d2c621a..09030a8347 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java @@ -11,42 +11,23 @@ import javax.servlet.ServletRegistration; public class WebInitializer implements WebApplicationInitializer { - public void onStartup(ServletContext container) throws ServletException { + public void onStartup(ServletContext container) throws ServletException { - AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); - ctx.register(ApplicationConfiguration.class); - //ctx.register(ThymeleafConfiguration.class); - //ctx.register(FreemarkerConfiguration.class); - //ctx.register(GroovyConfiguration.class); - //ctx.register(JadeTemplateConfiguration.class); - ctx.setServletContext(container); + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.register(ApplicationConfiguration.class); + // ctx.register(ThymeleafConfiguration.class); + // ctx.register(FreemarkerConfiguration.class); + // ctx.register(GroovyConfiguration.class); + // ctx.register(JadeTemplateConfiguration.class); + // ctx.register(PushConfiguration.class); + // ctx.setServletContext(container); - // Manage the lifecycle of the root application context - container.addListener(new ContextLoaderListener(ctx)); + // Manage the lifecycle of the root application context + container.addListener(new ContextLoaderListener(ctx)); - ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx)); + ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx)); - servlet.setLoadOnStartup(1); - servlet.addMapping("/"); - - } -// @Override -// public void onStartup(ServletContext container) { -// // Create the 'root' Spring application context -// AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); -// rootContext.register(ServiceConfig.class, JPAConfig.class, SecurityConfig.class); -// -// // Manage the lifecycle of the root application context -// container.addListener(new ContextLoaderListener(rootContext)); -// -// // Create the dispatcher servlet's Spring application context -// AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext(); -// dispatcherServlet.register(MvcConfig.class); -// -// // Register and map the dispatcher servlet -// ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet)); -// dispatcher.setLoadOnStartup(1); -// dispatcher.addMapping("/"); -// -// } + servlet.setLoadOnStartup(1); + servlet.addMapping("/"); + } } diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/AnnotationMethodHandlerAdapterExample.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/AnnotationMethodHandlerAdapterExample.java index 164830cd6a..89f5b2501b 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/AnnotationMethodHandlerAdapterExample.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/AnnotationMethodHandlerAdapterExample.java @@ -6,10 +6,10 @@ import org.springframework.web.servlet.ModelAndView; @Controller public class AnnotationMethodHandlerAdapterExample { - @RequestMapping("/annotedName") - public ModelAndView getEmployeeName() { - ModelAndView model = new ModelAndView("Greeting"); - model.addObject("message", "Dinesh"); - return model; - } + @RequestMapping("/annotedName") + public ModelAndView getEmployeeName() { + ModelAndView model = new ModelAndView("Greeting"); + model.addObject("message", "Dinesh"); + return model; + } } \ No newline at end of file diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java index 47af2ab50d..2ca9e2b135 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java @@ -23,13 +23,13 @@ public class FileUploadController implements HandlerExceptionResolver { } @RequestMapping(value = "/uploadFile", method = RequestMethod.POST) - public ModelAndView uploadFile(MultipartFile file) throws IOException{ + public ModelAndView uploadFile(MultipartFile file) throws IOException { ModelAndView modelAndView = new ModelAndView("file"); - - InputStream in = file.getInputStream(); + + InputStream in = file.getInputStream(); File currDir = new File("."); String path = currDir.getAbsolutePath(); - FileOutputStream f = new FileOutputStream(path.substring(0, path.length()-1)+ file.getOriginalFilename()); + FileOutputStream f = new FileOutputStream(path.substring(0, path.length() - 1) + file.getOriginalFilename()); int ch = 0; while ((ch = in.read()) != -1) { f.write(ch); @@ -37,15 +37,17 @@ public class FileUploadController implements HandlerExceptionResolver { f.flush(); f.close(); - modelAndView.getModel().put("message", "File uploaded successfully!"); + modelAndView.getModel() + .put("message", "File uploaded successfully!"); return modelAndView; } - + @Override - public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception exc) { + public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception exc) { ModelAndView modelAndView = new ModelAndView("file"); if (exc instanceof MaxUploadSizeExceededException) { - modelAndView.getModel().put("message", "File size exceeds limit!"); + modelAndView.getModel() + .put("message", "File size exceeds limit!"); } return modelAndView; } diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/RequestMappingHandlerAdapterExample.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/RequestMappingHandlerAdapterExample.java index 76ac3e2806..754bea79f1 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/RequestMappingHandlerAdapterExample.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/RequestMappingHandlerAdapterExample.java @@ -6,10 +6,10 @@ import org.springframework.web.servlet.ModelAndView; @Controller public class RequestMappingHandlerAdapterExample { - @RequestMapping("/requestName") - public ModelAndView getEmployeeName() { - ModelAndView model = new ModelAndView("Greeting"); - model.addObject("message", "Madhwal"); - return model; - } + @RequestMapping("/requestName") + public ModelAndView getEmployeeName() { + ModelAndView model = new ModelAndView("Greeting"); + model.addObject("message", "Madhwal"); + return model; + } } \ No newline at end of file diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java index bac091ffeb..17c4ab689e 100644 --- a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java @@ -6,13 +6,11 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; -public class SimpleControllerHandlerAdapterExample extends - AbstractController { - @Override - protected ModelAndView handleRequestInternal(HttpServletRequest request, - HttpServletResponse response) throws Exception { - ModelAndView model = new ModelAndView("Greeting"); - model.addObject("message", "Dinesh Madhwal"); - return model; - } +public class SimpleControllerHandlerAdapterExample extends AbstractController { + @Override + protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { + ModelAndView model = new ModelAndView("Greeting"); + model.addObject("message", "Dinesh Madhwal"); + return model; + } } \ No newline at end of file diff --git a/spring-mvc-push/src/main/java/com/baeldung/controller/PushController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/push/PushController.java similarity index 92% rename from spring-mvc-push/src/main/java/com/baeldung/controller/PushController.java rename to spring-mvc-simple/src/main/java/com/baeldung/spring/controller/push/PushController.java index c4698fe976..b557c65c93 100644 --- a/spring-mvc-push/src/main/java/com/baeldung/controller/PushController.java +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/push/PushController.java @@ -1,4 +1,4 @@ -package com.baeldung.controller; +package com.baeldung.spring.controller.push; import javax.servlet.http.PushBuilder; diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java new file mode 100644 index 0000000000..cfbd33cd57 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java @@ -0,0 +1,54 @@ +package com.baeldung.spring.controller.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/spring-mvc-simple/rss"); + channel.setTitle("Article Feed"); + channel.setDescription("Article Feed Description"); + channel.setPubDate(new Date()); + return channel; + } + + @Override + protected List buildFeedItems(Map map, HttpServletRequest httpStRequest, HttpServletResponse httpStResponse) throws Exception { + List list = new ArrayList(); + + 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; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java new file mode 100644 index 0000000000..1f51b238ac --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java @@ -0,0 +1,14 @@ +package com.baeldung.spring.controller.rss; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class ArticleRssController { + + @GetMapping(value = "/rss", produces = "application/*") + public String articleFeed() { + return "articleFeedView"; + } + +} diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml deleted file mode 100644 index 430b849012..0000000000 --- a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml deleted file mode 100644 index d3783c2e67..0000000000 --- a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml deleted file mode 100644 index 1d6e5628df..0000000000 --- a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/spring-mvc-push/src/main/webapp/WEB-INF/views/demo.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/demo.jsp similarity index 87% rename from spring-mvc-push/src/main/webapp/WEB-INF/views/demo.jsp rename to spring-mvc-simple/src/main/webapp/WEB-INF/views/demo.jsp index 28b27322ae..d5579debf7 100644 --- a/spring-mvc-push/src/main/webapp/WEB-INF/views/demo.jsp +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/demo.jsp @@ -12,7 +12,6 @@ " alt="Logo" height="126" width="411">
-
Go to - index + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/registration-jade.jade b/spring-mvc-simple/src/main/webapp/WEB-INF/views/registration-jade.jade index 44b6293ff0..f271ac6852 100644 --- a/spring-mvc-simple/src/main/webapp/WEB-INF/views/registration-jade.jade +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/registration-jade.jade @@ -4,7 +4,7 @@ html title User Registration body form(action="register" method="post" ) - label(for="email") Emailaaaaaaaa: + label(for="email") Email: input(type="text" name="email") label(for="password") Password: input(type="password" name="password") diff --git a/spring-mvc-push/src/main/webapp/resources/logo.png b/spring-mvc-simple/src/main/webapp/resources/logo.png similarity index 100% rename from spring-mvc-push/src/main/webapp/resources/logo.png rename to spring-mvc-simple/src/main/webapp/resources/logo.png diff --git a/spring-mvc-push/src/test/java/com/baeldung/controller/PushControllerIntegrationTest.java b/spring-mvc-simple/src/test/java/com/baeldung/controller/push/PushControllerIntegrationTest.java similarity index 90% rename from spring-mvc-push/src/test/java/com/baeldung/controller/PushControllerIntegrationTest.java rename to spring-mvc-simple/src/test/java/com/baeldung/controller/push/PushControllerIntegrationTest.java index 570e05cad6..a03d02895f 100644 --- a/spring-mvc-push/src/test/java/com/baeldung/controller/PushControllerIntegrationTest.java +++ b/spring-mvc-simple/src/test/java/com/baeldung/controller/push/PushControllerIntegrationTest.java @@ -1,10 +1,9 @@ -package com.baeldung.controller; +package com.baeldung.controller.push; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; @@ -12,9 +11,8 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.config.PushConfiguration; +import com.baeldung.spring.configuration.PushConfiguration; -@Disabled @SpringJUnitWebConfig(PushConfiguration.class) public class PushControllerIntegrationTest { @Autowired diff --git a/spring-mvc-xml/src/main/resources/contentManagementWebMvcConfig.xml b/spring-mvc-xml/src/main/resources/contentManagementWebMvcConfig.xml index 41422d8954..75d5c1ecd6 100644 --- a/spring-mvc-xml/src/main/resources/contentManagementWebMvcConfig.xml +++ b/spring-mvc-xml/src/main/resources/contentManagementWebMvcConfig.xml @@ -4,11 +4,11 @@ xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-4.2.xsd + http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-4.2.xsd + http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc - http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd"> + http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"> diff --git a/spring-mvc-xml/src/main/resources/webMvcConfig.xml b/spring-mvc-xml/src/main/resources/webMvcConfig.xml index c471adf331..37aebe1d1d 100644 --- a/spring-mvc-xml/src/main/resources/webMvcConfig.xml +++ b/spring-mvc-xml/src/main/resources/webMvcConfig.xml @@ -3,11 +3,11 @@ xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-4.2.xsd + http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-4.2.xsd + http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc - http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd" + http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd" > diff --git a/spring-mvc-xml/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-mvc-xml/src/main/webapp/WEB-INF/mvc-servlet.xml index 47e2769902..032457da43 100644 --- a/spring-mvc-xml/src/main/webapp/WEB-INF/mvc-servlet.xml +++ b/spring-mvc-xml/src/main/webapp/WEB-INF/mvc-servlet.xml @@ -2,9 +2,9 @@ + http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/spring-reactive-websocket/pom.xml b/spring-reactive-websocket/pom.xml deleted file mode 100644 index 846cece177..0000000000 --- a/spring-reactive-websocket/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - 4.0.0 - - spring-reactive-websocket - 0.0.1-SNAPSHOT - jar - - spring-reactive-websocket - Reactive WebSockets with Spring 5 - - - com.baeldung - parent-modules - 1.0.0-SNAPSHOT - - - - UTF-8 - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-starter-integration - 2.0.0.M7 - - - org.springframework.boot - spring-boot-starter-webflux - 2.0.0.M7 - - - org.projectlombok - lombok - compile - RELEASE - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java deleted file mode 100644 index f8952d750d..0000000000 --- a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.baeldung; - -import java.net.URI; -import java.time.Duration; - -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.socket.WebSocketMessage; -import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; -import org.springframework.web.reactive.socket.client.WebSocketClient; - -import reactor.core.publisher.Mono; - -@SpringBootApplication -public class ReactiveWebSocketApplication { - public static void main(String[] args) { - SpringApplication.run(ReactiveWebSocketApplication.class, args); - } - - /** - * Spring Reactive WebSocket Client - * **/ - @Bean - CommandLineRunner runner() { - return run -> { - 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"))) - .thenMany(session.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()) - .then()) - .block(); -// .block(Duration.ofSeconds(10L));//force timeout after given duration - }; - } -} diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java deleted file mode 100644 index 4a548322b3..0000000000 --- a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.baeldung; - -import org.springframework.web.reactive.socket.WebSocketSession; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.WebSocketMessage; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import javax.annotation.PostConstruct; -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.UUID; - -@Component -public class ReactiveWebSocketHandler implements WebSocketHandler { - - private Flux eventFlux; - private Flux intervalFlux; - - /** - * Here we prepare a Flux that will emit a message every second - */ - @PostConstruct - private void init() throws InterruptedException { - - eventFlux = Flux.generate(e -> { - Event event = new Event(UUID.randomUUID() - .toString(), - LocalDateTime.now() - .toString()); - e.next(event); - }); - - intervalFlux = Flux.interval(Duration.ofMillis(1000L)) - .zipWith(eventFlux, (time, event) -> event); - - } - - /** - * On each new client session, send the message flux to the client. - * Spring subscribes to the flux and send every new flux event to the WebSocketSession object - * @param session - * @return Mono - */ - @Override - public Mono handle(WebSocketSession webSocketSession) { - ObjectMapper json = new ObjectMapper(); - return webSocketSession.send(intervalFlux.map(event -> { - try { - String jsonEvent = json.writeValueAsString(event); - System.out.println(jsonEvent); - return jsonEvent; - } catch (JsonProcessingException e) { - e.printStackTrace(); - return ""; - } - }) - .map(webSocketSession::textMessage)) - - .and(webSocketSession.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()); - } - -} diff --git a/spring-rest-embedded-tomcat/pom.xml b/spring-rest-embedded-tomcat/pom.xml index cee9933c0d..3622e84101 100644 --- a/spring-rest-embedded-tomcat/pom.xml +++ b/spring-rest-embedded-tomcat/pom.xml @@ -84,14 +84,13 @@ **/JdbcTest.java **/*LiveTest.java -
- 5.0.1.RELEASE + 5.0.2.RELEASE 2.19.1 4.12 2.9.2 diff --git a/spring-rest-simple/README.md b/spring-rest-simple/README.md index 916676feb5..982e16d5a5 100644 --- a/spring-rest-simple/README.md +++ b/spring-rest-simple/README.md @@ -5,3 +5,4 @@ - [The Guide to RestTemplate](http://www.baeldung.com/rest-template) - [Spring RequestMapping](http://www.baeldung.com/spring-requestmapping) - [ETags for REST with Spring](http://www.baeldung.com/etags-for-rest-with-spring) +- [Spring and Apache FileUpload](http://www.baeldung.com/spring-apache-file-upload) diff --git a/spring-rest/pom.xml b/spring-rest/pom.xml index 6da891b054..50ac00e1c3 100644 --- a/spring-rest/pom.xml +++ b/spring-rest/pom.xml @@ -169,6 +169,12 @@ commons-io 2.4 + + + au.com.dius + pact-jvm-provider-junit_2.11 + ${pact.version} + @@ -324,6 +330,8 @@ 3.4.1 2.2.0 + + 3.5.11 diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/PactController.java b/spring-rest/src/main/java/org/baeldung/web/controller/PactController.java new file mode 100644 index 0000000000..4f586479ef --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/controller/PactController.java @@ -0,0 +1,32 @@ +package org.baeldung.web.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.baeldung.web.dto.PactDto; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class PactController { + + List pacts = new ArrayList<>(); + + @GetMapping(value = "/pact", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public PactDto getPact() { + return new PactDto(true, "tom"); + } + + @PostMapping("/pact") + @ResponseStatus(HttpStatus.CREATED) + public void createPact(PactDto pact) { + pacts.add(pact); + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/PactDto.java b/spring-rest/src/main/java/org/baeldung/web/dto/PactDto.java new file mode 100644 index 0000000000..e34e2bdf3b --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/PactDto.java @@ -0,0 +1,33 @@ +package org.baeldung.web.dto; + +public class PactDto { + + private boolean condition; + private String name; + + public PactDto() { + } + + public PactDto(boolean condition, String name) { + super(); + this.condition = condition; + this.name = name; + } + + public boolean isCondition() { + return condition; + } + + public void setCondition(boolean condition) { + this.condition = condition; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-rest/src/main/resources/pacts/test_consumer-test_provider.json b/spring-rest/src/main/resources/pacts/test_consumer-test_provider.json new file mode 100644 index 0000000000..c8082798f4 --- /dev/null +++ b/spring-rest/src/main/resources/pacts/test_consumer-test_provider.json @@ -0,0 +1,61 @@ +{ + "provider": { + "name": "test_provider" + }, + "consumer": { + "name": "test_consumer" + }, + "interactions": [ + { + "description": "GET REQUEST", + "request": { + "method": "GET", + "path": "/pact" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "condition": true, + "name": "tom" + } + }, + "providerStates": [ + { + "name": "test GET" + } + ] + }, + { + "description": "POST REQUEST", + "request": { + "method": "POST", + "path": "/pact", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "name": "Michael" + } + }, + "response": { + "status": 201 + }, + "providerStates": [ + { + "name": "test POST" + } + ] + } + ], + "metadata": { + "pact-specification": { + "version": "3.0.0" + }, + "pact-jvm": { + "version": "3.5.0" + } + } +} \ No newline at end of file diff --git a/spring-rest/src/test/java/org/baeldung/pact/PactProviderTest.java b/spring-rest/src/test/java/org/baeldung/pact/PactProviderTest.java new file mode 100644 index 0000000000..0456b0d3e7 --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/pact/PactProviderTest.java @@ -0,0 +1,40 @@ +package org.baeldung.pact; + +import org.baeldung.config.MainApplication; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringApplication; +import org.springframework.web.context.ConfigurableWebApplicationContext; + +import au.com.dius.pact.provider.junit.PactRunner; +import au.com.dius.pact.provider.junit.Provider; +import au.com.dius.pact.provider.junit.State; +import au.com.dius.pact.provider.junit.loader.PactFolder; +import au.com.dius.pact.provider.junit.target.HttpTarget; +import au.com.dius.pact.provider.junit.target.Target; +import au.com.dius.pact.provider.junit.target.TestTarget; + +@RunWith(PactRunner.class) +@Provider("test_provider") +@PactFolder("pacts") +public class PactProviderTest { + + @TestTarget + public final Target target = new HttpTarget("http", "localhost", 8082, "/spring-rest"); + + private static ConfigurableWebApplicationContext application; + + @BeforeClass + public static void start() { + application = (ConfigurableWebApplicationContext) SpringApplication.run(MainApplication.class); + } + + @State("test GET") + public void toGetState() { + } + + @State("test POST") + public void toPostState() { + } + +} diff --git a/testing-modules/README.md b/testing-modules/README.md index 3fbeea6188..ccf63a9815 100644 --- a/testing-modules/README.md +++ b/testing-modules/README.md @@ -1,3 +1,6 @@ ## Testing Modules +### Relevant Articles: + +- [Quick Guide to BDDMockito](http://www.baeldung.com/bdd-mockito)