From f55bd014f9704d5fbb18fb5a9f227f571fa30a39 Mon Sep 17 00:00:00 2001 From: Thoughtscript Date: Tue, 21 Nov 2017 06:44:51 +0000 Subject: [PATCH 001/118] BAEL-1175 - Using a Spring Cloud App Starter Bash --- spring-cloud/README.md | 1 + .../bash/hadoop.sh | 42 +++++++++++++++++++ .../hdfs/application.properties | 1 + .../spring-cloud-stream-starters/hdfs/hdfs.sh | 14 +++++++ .../twitter/application.properties | 4 ++ .../twitter/twitter.sh | 15 +++++++ 6 files changed, 77 insertions(+) create mode 100644 spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh create mode 100644 spring-cloud/spring-cloud-stream-starters/hdfs/application.properties create mode 100644 spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh create mode 100644 spring-cloud/spring-cloud-stream-starters/twitter/application.properties create mode 100644 spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh diff --git a/spring-cloud/README.md b/spring-cloud/README.md index 1b793144b1..523cf683c3 100644 --- a/spring-cloud/README.md +++ b/spring-cloud/README.md @@ -19,3 +19,4 @@ ### 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) + [Using a Spring Cloud App Starter](http://www.baeldung.com/using-a-spring-cloud-app-starter) \ No newline at end of file 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..5eebcca426 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh @@ -0,0 +1,42 @@ +# 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 local user + +# Start NameNode daemon and DataNode daemon +start-dfs.sh +# stop-dfs.sh + +# Install Maven +sudo apt-get install maven 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..8b421f954c --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties @@ -0,0 +1 @@ +hdfs.fs-uri=http://osboxes:50075 \ 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..3c45391f75 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -0,0 +1,14 @@ +# Git spring-cloud-stream-app-starters +git clone https://github.com/spring-cloud/spring-cloud-stream-app-starters.git + +# Navigate to HDFS +cd /hdfs + +# Navigate to the the app configuration settings +cd /spring-cloud-starter-stream-sink-hdfs/src/main/java/org/springframework/cloud/stream/app/hdfs/sink/HdfsSinkProperties.java +# Specify the properties you want there +# or inject the application.properties file before building the app + +# Then build the customized starter app +cd ../../../../../../../../../../../../../ +mvn clean install \ 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..5cfedee80e --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties @@ -0,0 +1,4 @@ +twitter.credentials.access-token=932486336086286336-2HURQbA2cYzX5hixgAshIBy2Dhefupn +twitter.credentials.access-token-secret=0pyZ7etHvro8x85QSXxsqYFzYk63bK6DS5nNYy0R3l1io +twitter.credentials.consumer-key=10xCXkRYi5xLFYq3P0ymWGEwJ +twitter.credentials.consumer-secret=VfyCUcGfAQ2aWcd3uTg8GmVGyhUfAcNJU6ksG09TAtPMqhmWTS \ 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..c3d16bb92e --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -0,0 +1,15 @@ +# Git spring-cloud-stream-app-starters +git clone https://github.com/spring-cloud/spring-cloud-stream-app-starters.git + +# Navigate to Twitter +cd /twitter + +# Navigate to the the app configuration settings +cd /spring-cloud-starter-stream-source-twitterstream/src/main/java/org/springframework/cloud/stream/app/twitterstream/source/TwitterStreamProperties.java +cd /spring-cloud-stream-app-starters/app-starters-common/app-starters-twitter-common/src/main/java/org/springframework/cloud/stream/app/twitter/TwitterCredentials.java +# Specify the properties you want there +# or inject the application.properties file before building the app + +# Then build the customized starter app +cd ../../../../../../../../../../../../ +mvn clean install \ No newline at end of file From 4e8b83721b04f92daffc9c3f7c9284b1387a3014 Mon Sep 17 00:00:00 2001 From: Thoughtscript Date: Thu, 23 Nov 2017 00:54:45 +0000 Subject: [PATCH 002/118] BAEL-1175 - Updating scripts post-1.1.0 RC1 --- .../spring-cloud-stream-starters/hdfs/hdfs.sh | 19 +++++++---------- .../twitter/twitter.sh | 21 ++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh index 3c45391f75..a9df476ef4 100644 --- a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -1,14 +1,11 @@ # Git spring-cloud-stream-app-starters -git clone https://github.com/spring-cloud/spring-cloud-stream-app-starters.git +# 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 -# Navigate to HDFS -cd /hdfs +# Build it +./mvnw clean install -PgenerateApps -# Navigate to the the app configuration settings -cd /spring-cloud-starter-stream-sink-hdfs/src/main/java/org/springframework/cloud/stream/app/hdfs/sink/HdfsSinkProperties.java -# Specify the properties you want there -# or inject the application.properties file before building the app - -# Then build the customized starter app -cd ../../../../../../../../../../../../../ -mvn clean install \ No newline at end of file +# RUn it +cd apps +# Optionally inject application.properties prior to build +java -jar hdfs-sink.jar --fsUri=http://osboxes:50075 \ 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 index c3d16bb92e..994d40dd4c 100644 --- a/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -1,15 +1,12 @@ # Git spring-cloud-stream-app-starters -git clone https://github.com/spring-cloud/spring-cloud-stream-app-starters.git +# 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 -# Navigate to Twitter -cd /twitter +# Build it +./mvnw clean install -PgenerateApps -# Navigate to the the app configuration settings -cd /spring-cloud-starter-stream-source-twitterstream/src/main/java/org/springframework/cloud/stream/app/twitterstream/source/TwitterStreamProperties.java -cd /spring-cloud-stream-app-starters/app-starters-common/app-starters-twitter-common/src/main/java/org/springframework/cloud/stream/app/twitter/TwitterCredentials.java -# Specify the properties you want there -# or inject the application.properties file before building the app - -# Then build the customized starter app -cd ../../../../../../../../../../../../ -mvn clean install \ No newline at end of file +# 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 From 4331871e09966b5521600c9076617c7ad465b4b9 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Tue, 2 Jan 2018 15:21:09 +0100 Subject: [PATCH 003/118] BAEL-592 Guide to Hibernate 5 with Spring --- .../hibernate/bootstrap/BarHibernateDAO.java | 39 ++++++++++++ .../hibernate/bootstrap/HibernateConf.java | 61 +++++++++++++++++++ .../hibernate/bootstrap/HibernateXMLConf.java | 24 ++++++++ .../hibernate/bootstrap/model/TestEntity.java | 29 +++++++++ .../resources/hibernate5Configuration.xml | 30 +++++++++ .../main/resources/persistence-h2.properties | 1 + .../HibernateBootstrapIntegrationTest.java | 38 ++++++++++++ .../HibernateXMLBootstrapIntegrationTest.java | 36 +++++++++++ 8 files changed, 258 insertions(+) create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/BarHibernateDAO.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateConf.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateXMLConf.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/model/TestEntity.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/resources/hibernate5Configuration.xml create mode 100644 persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java create mode 100644 persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateXMLBootstrapIntegrationTest.java diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/BarHibernateDAO.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/BarHibernateDAO.java new file mode 100644 index 0000000000..5fc932b256 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/BarHibernateDAO.java @@ -0,0 +1,39 @@ +package com.baeldung.hibernate.bootstrap; + +import com.baeldung.hibernate.bootstrap.model.TestEntity; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class BarHibernateDAO { + + @Autowired + private SessionFactory sessionFactory; + + public TestEntity findEntity(int id) { + + return getCurrentSession().find(TestEntity.class, 1); + } + + public void createEntity(TestEntity entity) { + + getCurrentSession().save(entity); + } + + public void createEntity(int id, String newDescription) { + + TestEntity entity = findEntity(id); + entity.setDescription(newDescription); + getCurrentSession().save(entity); + } + + public void deleteEntity(int id) { + + TestEntity entity = findEntity(id); + getCurrentSession().delete(entity); + } + + protected Session getCurrentSession() { + return sessionFactory.getCurrentSession(); + } +} diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateConf.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateConf.java new file mode 100644 index 0000000000..150e3778af --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateConf.java @@ -0,0 +1,61 @@ +package com.baeldung.hibernate.bootstrap; + +import com.google.common.base.Preconditions; +import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.orm.hibernate5.HibernateTransactionManager; +import org.springframework.orm.hibernate5.LocalSessionFactoryBean; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableTransactionManagement +@PropertySource({ "classpath:persistence-h2.properties" }) +public class HibernateConf { + + @Autowired + private Environment env; + + @Bean + public LocalSessionFactoryBean sessionFactory() { + final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); + sessionFactory.setDataSource(dataSource()); + sessionFactory.setPackagesToScan(new String[] { "com.baeldung.hibernate.bootstrap.model" }); + sessionFactory.setHibernateProperties(hibernateProperties()); + + return sessionFactory; + } + + @Bean + public DataSource dataSource() { + final BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName"))); + dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url"))); + dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user"))); + dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("jdbc.pass"))); + + return dataSource; + } + + @Bean + public PlatformTransactionManager hibernateTransactionManager() { + final HibernateTransactionManager transactionManager = new HibernateTransactionManager(); + transactionManager.setSessionFactory(sessionFactory().getObject()); + return transactionManager; + } + + private final Properties hibernateProperties() { + final Properties hibernateProperties = new Properties(); + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + + return hibernateProperties; + } +} diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateXMLConf.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateXMLConf.java new file mode 100644 index 0000000000..b3e979478f --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/HibernateXMLConf.java @@ -0,0 +1,24 @@ +package com.baeldung.hibernate.bootstrap; + +import com.google.common.base.Preconditions; +import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.orm.hibernate5.HibernateTransactionManager; +import org.springframework.orm.hibernate5.LocalSessionFactoryBean; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableTransactionManagement +@ImportResource({ "classpath:hibernate5Configuration.xml" }) +public class HibernateXMLConf { + +} diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/model/TestEntity.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/model/TestEntity.java new file mode 100644 index 0000000000..cae41db831 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernate/bootstrap/model/TestEntity.java @@ -0,0 +1,29 @@ +package com.baeldung.hibernate.bootstrap.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class TestEntity { + + private int id; + + private String description; + + @Id + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/persistence-modules/spring-hibernate-5/src/main/resources/hibernate5Configuration.xml b/persistence-modules/spring-hibernate-5/src/main/resources/hibernate5Configuration.xml new file mode 100644 index 0000000000..cb6cf0aa5c --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/resources/hibernate5Configuration.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + ${hibernate.hbm2ddl.auto} + ${hibernate.dialect} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties b/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties index 915bc4317b..0325174b67 100644 --- a/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties +++ b/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties @@ -2,6 +2,7 @@ jdbc.driverClassName=org.h2.Driver jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 jdbc.eventGeneratedId=sa +jdbc.user=sa jdbc.pass=sa # hibernate.X diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java new file mode 100644 index 0000000000..ffe82b7ced --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java @@ -0,0 +1,38 @@ +package com.baeldung.hibernate.bootstrap; + +import com.baeldung.hibernate.bootstrap.model.TestEntity; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { HibernateConf.class }) +@Transactional +public class HibernateBootstrapIntegrationTest { + + @Autowired + private SessionFactory sessionFactory; + + @Test + public void whenBootstrapHibernateSession_thenNoException() { + + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + } + +} diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateXMLBootstrapIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateXMLBootstrapIntegrationTest.java new file mode 100644 index 0000000000..5b811ad576 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateXMLBootstrapIntegrationTest.java @@ -0,0 +1,36 @@ +package com.baeldung.hibernate.bootstrap; + +import com.baeldung.hibernate.bootstrap.model.TestEntity; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { HibernateXMLConf.class }) +@Transactional +public class HibernateXMLBootstrapIntegrationTest { + + @Autowired + private SessionFactory sessionFactory; + + @Test + public void whenBootstrapHibernateSession_thenNoException() { + + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + } + +} From 02b0c1d2933ee636241ae1446773af770f68dd13 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Thu, 4 Jan 2018 10:26:48 +0100 Subject: [PATCH 004/118] BAEL-592: Upgrade to Spring 5 --- persistence-modules/spring-hibernate-5/pom.xml | 2 +- .../com/baeldung/manytomany/spring/PersistenceConfig.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/persistence-modules/spring-hibernate-5/pom.xml b/persistence-modules/spring-hibernate-5/pom.xml index 88db38b2fc..86e952c0e4 100644 --- a/persistence-modules/spring-hibernate-5/pom.xml +++ b/persistence-modules/spring-hibernate-5/pom.xml @@ -179,7 +179,7 @@ - 4.3.10.RELEASE + 5.0.2.RELEASE 1.10.6.RELEASE diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/manytomany/spring/PersistenceConfig.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/manytomany/spring/PersistenceConfig.java index 6f359054b6..5dace1f742 100644 --- a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/manytomany/spring/PersistenceConfig.java +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/manytomany/spring/PersistenceConfig.java @@ -10,8 +10,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; -import org.springframework.orm.hibernate4.HibernateTransactionManager; -import org.springframework.orm.hibernate4.LocalSessionFactoryBean; +import org.springframework.orm.hibernate5.HibernateTransactionManager; +import org.springframework.orm.hibernate5.LocalSessionFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; From c174946f855ff445c787c8ddac683b73e26a0378 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Mon, 8 Jan 2018 22:12:47 +0100 Subject: [PATCH 005/118] Code for Alessio Stalla's evaluation article (Different Types of Bean Injection in Spring) --- .../baeldung/typesofbeaninjection/Config.java | 16 +++++ .../typesofbeaninjection/domain/Car.java | 24 ++++++++ .../typesofbeaninjection/domain/Engine.java | 24 ++++++++ .../domain/autowired/constructor/Car.java | 30 ++++++++++ .../domain/autowired/properties/Car.java | 30 ++++++++++ .../typesofbeaninjection-context.xml | 19 ++++++ .../TypeOfBeanInjectionUnitTest.java | 58 +++++++++++++++++++ 7 files changed, 201 insertions(+) create mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java create mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java create mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java create mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java create mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java create mode 100644 spring-core/src/main/resources/typesofbeaninjection-context.xml create mode 100644 spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java new file mode 100644 index 0000000000..a967a63005 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java @@ -0,0 +1,16 @@ +package com.baeldung.typesofbeaninjection; + +import com.baeldung.typesofbeaninjection.domain.Engine; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan("com.baeldung.typesofbeaninjection") +public class Config { + + @Bean + public Engine engine() { + return new Engine("V8", 5); + } +} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java new file mode 100644 index 0000000000..878f369c8e --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java @@ -0,0 +1,24 @@ +package com.baeldung.typesofbeaninjection.domain; + +public class Car { + private Engine engine; + + public Car() {} + + public Car(Engine engine) { + this.engine = engine; + } + + public Engine getEngine() { + return engine; + } + + public void setEngine(Engine engine) { + this.engine = engine; + } + + @Override + public String toString() { + return String.format("Car with %s engine", engine); + } +} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java new file mode 100644 index 0000000000..79e7a9856d --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java @@ -0,0 +1,24 @@ +package com.baeldung.typesofbeaninjection.domain; + +public class Engine { + private final String type; + private final int volume; + + public Engine(String type, int volume) { + this.type = type; + this.volume = volume; + } + + public String getType() { + return type; + } + + public int getVolume() { + return volume; + } + + @Override + public String toString() { + return String.format("%s %d", type, volume); + } +} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java new file mode 100644 index 0000000000..33fadf8d2b --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java @@ -0,0 +1,30 @@ +package com.baeldung.typesofbeaninjection.domain.autowired.constructor; + +import com.baeldung.typesofbeaninjection.domain.Engine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("car-autowired-by-constructor") +public class Car { + private Engine engine; + + public Car() {} + + @Autowired + public Car(Engine engine) { + this.engine = engine; + } + + public Engine getEngine() { + return engine; + } + + public void setEngine(Engine engine) { + this.engine = engine; + } + + @Override + public String toString() { + return String.format("Car with %s engine", engine); + } +} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java new file mode 100644 index 0000000000..1df33f5062 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java @@ -0,0 +1,30 @@ +package com.baeldung.typesofbeaninjection.domain.autowired.properties; + +import com.baeldung.typesofbeaninjection.domain.Engine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("car-autowired-by-properties") +public class Car { + private Engine engine; + + public Car() {} + + public Car(Engine engine) { + this.engine = engine; + } + + public Engine getEngine() { + return engine; + } + + @Autowired + public void setEngine(Engine engine) { + this.engine = engine; + } + + @Override + public String toString() { + return String.format("Car with %s engine", engine); + } +} diff --git a/spring-core/src/main/resources/typesofbeaninjection-context.xml b/spring-core/src/main/resources/typesofbeaninjection-context.xml new file mode 100644 index 0000000000..5a9a0b59d7 --- /dev/null +++ b/spring-core/src/main/resources/typesofbeaninjection-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java b/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java new file mode 100644 index 0000000000..1b1319e001 --- /dev/null +++ b/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java @@ -0,0 +1,58 @@ +package com.baeldung.typesofbeaninjection; + +import com.baeldung.typesofbeaninjection.domain.Car; +import com.baeldung.typesofbeaninjection.domain.Engine; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TypeOfBeanInjectionUnitTest { + + @Test + public void whenConstructionInjectionInXML_thenCarHasEngine() { + ApplicationContext applicationContext + = new ClassPathXmlApplicationContext("/typesofbeaninjection-context.xml"); + Car car = applicationContext.getBean("alices-car", Car.class); + + checkEngine(car.getEngine()); + } + + @Test + public void whenPropertyInjectionInXML_thenCarHasEngine() { + ApplicationContext applicationContext + = new ClassPathXmlApplicationContext("/typesofbeaninjection-context.xml"); + Car car = applicationContext.getBean("bobs-car", Car.class); + + checkEngine(car.getEngine()); + } + + @Test + public void whenConstructionInjectionAnnotations_thenCarHasEngine() { + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); + com.baeldung.typesofbeaninjection.domain.autowired.constructor.Car car + = applicationContext.getBean(com.baeldung.typesofbeaninjection.domain.autowired.constructor.Car.class); + + checkEngine(car.getEngine()); + } + + @Test + public void whenPropertyInjectionAnnotations_thenCarHasEngine() { + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); + com.baeldung.typesofbeaninjection.domain.autowired.properties.Car car + = applicationContext.getBean(com.baeldung.typesofbeaninjection.domain.autowired.properties.Car.class); + + checkEngine(car.getEngine()); + } + + private void checkEngine(Engine engine) { + assertNotNull(engine); + assertEquals("V8", engine.getType()); + assertEquals(5, engine.getVolume()); + } + + +} From b41ebab6b8dc0c7a95873329633dc68cd649db24 Mon Sep 17 00:00:00 2001 From: felipeazv Date: Fri, 12 Jan 2018 23:04:21 +0100 Subject: [PATCH 006/118] BAEL-1475: Reactive WebSockets with Spring 5 --- spring-reactive-websocket/pom.xml | 92 +++++++++++++++++++ .../src/main/java/com/baeldung/Event.java | 11 +++ .../ReactiveWebSocketApplication.java | 38 ++++++++ .../ReactiveWebSocketConfiguration.java | 34 +++++++ .../baeldung/ReactiveWebSocketHandler.java | 71 ++++++++++++++ .../resources/static/client-websocket.html | 34 +++++++ 6 files changed, 280 insertions(+) create mode 100644 spring-reactive-websocket/pom.xml create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/Event.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java create mode 100644 spring-reactive-websocket/src/main/resources/static/client-websocket.html diff --git a/spring-reactive-websocket/pom.xml b/spring-reactive-websocket/pom.xml new file mode 100644 index 0000000000..b234e0345e --- /dev/null +++ b/spring-reactive-websocket/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + spring-reactive-websocket + 0.0.1-SNAPSHOT + jar + + spring-reactive-websocket + Example from article: 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/Event.java b/spring-reactive-websocket/src/main/java/com/baeldung/Event.java new file mode 100644 index 0000000000..20d678c214 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/Event.java @@ -0,0 +1,11 @@ +package com.baeldung; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Event { + private String eventId; + private String eventDt; +} diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java new file mode 100644 index 0000000000..f8952d750d --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java @@ -0,0 +1,38 @@ +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/ReactiveWebSocketConfiguration.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java new file mode 100644 index 0000000000..6729e09273 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java @@ -0,0 +1,34 @@ +package com.baeldung; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class ReactiveWebSocketConfiguration { + + @Autowired + private WebSocketHandler webSocketHandler; + + @Bean + public HandlerMapping webSocketHandlerMapping() { + Map map = new HashMap<>(); + map.put("/event-emitter", webSocketHandler); + + SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping(); + handlerMapping.setOrder(1); + handlerMapping.setUrlMap(map); + return handlerMapping; + } + + @Bean + public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); + } +} \ No newline at end of file diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java new file mode 100644 index 0000000000..4a548322b3 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java @@ -0,0 +1,71 @@ +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-reactive-websocket/src/main/resources/static/client-websocket.html b/spring-reactive-websocket/src/main/resources/static/client-websocket.html new file mode 100644 index 0000000000..3f840e8bd4 --- /dev/null +++ b/spring-reactive-websocket/src/main/resources/static/client-websocket.html @@ -0,0 +1,34 @@ + + + + +Baeldung: Spring 5 Reactive Client WebSocket (Browser) + + + +
+ + + \ No newline at end of file From 466340e96f49897519ffe0d94eef2ccd3320fa2a Mon Sep 17 00:00:00 2001 From: felipeazv Date: Fri, 12 Jan 2018 23:12:20 +0100 Subject: [PATCH 007/118] BAEL-1475: Reactive WebSockets with Spring 5 --- spring-reactive-websocket/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-reactive-websocket/pom.xml b/spring-reactive-websocket/pom.xml index b234e0345e..846cece177 100644 --- a/spring-reactive-websocket/pom.xml +++ b/spring-reactive-websocket/pom.xml @@ -8,7 +8,7 @@ jar spring-reactive-websocket - Example from article: Reactive WebSockets with Spring 5 + Reactive WebSockets with Spring 5 com.baeldung From 5b8e84b26ccb063e67e9f21be53d0740820ed808 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Sun, 14 Jan 2018 20:30:32 +0530 Subject: [PATCH 008/118] Refaactoring-Thread Interrupt (#3412) --- .../com/baeldung/concurrent/stopping/ControlSubThread.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java index 0e72821a88..e6522168bb 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java @@ -43,8 +43,9 @@ public class ControlSubThread implements Runnable { try { Thread.sleep(interval); } catch (InterruptedException e) { - // no-op, just loop again - } + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted, Failed to complete operation"); + } // do something } stopped.set(true); From fb141b9b3fac840db0292199c95a269745e02a3f Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Mon, 15 Jan 2018 00:33:18 +0530 Subject: [PATCH 009/118] Bael 1469 (#3414) * Changes for BAEL-1469 Upgrade the Spring MVC article * web.xml changes for servlet 3.1 --- spring-mvc-xml/src/main/webapp/WEB-INF/web.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml b/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml index 1ea3051426..6ff435b84b 100644 --- a/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml +++ b/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml @@ -1,10 +1,9 @@ - - Spring MVC XML Application @@ -65,4 +64,4 @@ /errors - \ No newline at end of file + From 0d4a49f4f1ea17d6acbb42fd0935193df374fb9f Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sun, 14 Jan 2018 22:27:54 +0200 Subject: [PATCH 010/118] Create README.md --- spring-5-security/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 spring-5-security/README.md diff --git a/spring-5-security/README.md b/spring-5-security/README.md new file mode 100644 index 0000000000..1c9fad65e4 --- /dev/null +++ b/spring-5-security/README.md @@ -0,0 +1,3 @@ +## Relevant articles: + +- [Spring Security 5 -OAuth2 Login](http://www.baeldung.com/spring-security-5-oauth2-login) From 22dbf2c8cd363c25220e03a194e31f8c3502f536 Mon Sep 17 00:00:00 2001 From: Eric Goebelbecker Date: Sun, 14 Jan 2018 16:48:42 -0500 Subject: [PATCH 011/118] Bael-1364 - Introduction to Lettuce (#3415) --- .../influxdb/InfluxDBConnectionLiveTest.java | 2 +- persistence-modules/redis/README.md | 2 + persistence-modules/redis/pom.xml | 7 + .../baeldung/LettuceIntegrationLiveTest.java | 312 ++++++++++++++++++ 4 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java diff --git a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java index 50d35b9b1c..6c7a03cb70 100644 --- a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java +++ b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java @@ -55,7 +55,7 @@ public class InfluxDBConnectionLiveTest { InfluxDB connection = connectDatabase(); - // Create "baeldung and check for it + // Create "baeldung" and check for it connection.createDatabase("baeldung"); assertTrue(connection.databaseExists("baeldung")); diff --git a/persistence-modules/redis/README.md b/persistence-modules/redis/README.md index d179b80c33..dd655ca7aa 100644 --- a/persistence-modules/redis/README.md +++ b/persistence-modules/redis/README.md @@ -1,3 +1,5 @@ ### Relevant Articles: - [Intro to Jedis – the Java Redis Client Library](http://www.baeldung.com/jedis-java-redis-client-library) - [A Guide to Redis with Redisson](http://www.baeldung.com/redis-redisson) +- [Intro to Lettuce – the Java Redis Client Library](http://www.baeldung.com/lettuce-java-redis-client-library) + diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml index 4321b491eb..1f27faa09a 100644 --- a/persistence-modules/redis/pom.xml +++ b/persistence-modules/redis/pom.xml @@ -36,6 +36,13 @@ redisson 3.3.0 + + + io.lettuce + lettuce-core + 5.0.1.RELEASE + + diff --git a/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java b/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java new file mode 100644 index 0000000000..eb879d1d21 --- /dev/null +++ b/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java @@ -0,0 +1,312 @@ +package com.baeldung; + +import io.lettuce.core.LettuceFutures; +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.TransactionResult; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.pubsub.RedisPubSubListener; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.async.RedisPubSubAsyncCommands; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; + +public class LettuceIntegrationLiveTest { + + private static Logger log = LoggerFactory.getLogger(LettuceIntegrationLiveTest.class); + + private static StatefulRedisConnection redisConnection; + + private static RedisClient redisClient; + + @BeforeClass + public static void setUp() { + // Docker defaults to mapping redis port to 32768 + redisClient = RedisClient.create("redis://localhost:32768/"); + redisConnection = redisClient.connect(); + } + + @AfterClass + public static void destroy() { + redisConnection.close(); + } + + @Test + public void givenAString_thenSaveItAsRedisStringsSync() { + + RedisCommands syncCommands = redisConnection.sync(); + + String key = "key"; + String value = "value"; + + syncCommands.set(key, value); + String response = syncCommands.get(key); + + Assert.assertEquals(value, response); + } + + @Test + public void givenValues_thenSaveAsRedisHashSync() { + + RedisCommands syncCommands = redisConnection.sync(); + + String recordName = "record1"; + String name = "FirstName"; + String value = "John"; + String surname = "LastName"; + String value1 = "Smith"; + + syncCommands.hset(recordName, name, value); + syncCommands.hset(recordName, surname, value1); + Map record = syncCommands.hgetall(recordName); + + Assert.assertEquals(record.get(name), value); + Assert.assertEquals(record.get(surname), value1); + } + + @Test + public void givenAString_thenSaveItAsRedisStringsAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String key = "key"; + String value = "value"; + + asyncCommands.set(key, value); + RedisFuture redisFuture = asyncCommands.get(key); + + String response = redisFuture.get(); + + Assert.assertEquals(value, response); + } + + @Test + public void givenValues_thenSaveAsRedisHashAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String recordName = "record1"; + String name = "FirstName"; + String value = "John"; + String surname = "LastName"; + String value1 = "Smith"; + + asyncCommands.hset(recordName, name, value); + asyncCommands.hset(recordName, surname, value1); + RedisFuture> redisFuture = asyncCommands.hgetall(recordName); + + Map record = redisFuture.get(); + + Assert.assertEquals(record.get(name), value); + Assert.assertEquals(record.get(surname), value1); + } + + @Test + public void givenValues_thenSaveAsRedisListAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String listName = "tasks"; + String firstTask = "firstTask"; + String secondTask = "secondTask"; + + asyncCommands.del(listName); + + asyncCommands.lpush(listName, firstTask); + asyncCommands.lpush(listName, secondTask); + RedisFuture redisFuture = asyncCommands.rpop(listName); + + String nextTask = redisFuture.get(); + + Assert.assertEquals(firstTask, nextTask); + + asyncCommands.del(listName); + + asyncCommands.lpush(listName, firstTask); + asyncCommands.lpush(listName, secondTask); + + redisFuture = asyncCommands.lpop(listName); + + nextTask = redisFuture.get(); + + Assert.assertEquals(secondTask, nextTask); + + } + + @Test + public void givenSetElements_thenSaveThemInRedisSetAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String countries = "countries"; + + String countryOne = "Spain"; + String countryTwo = "Ireland"; + String countryThree = "Ireland"; + + asyncCommands.sadd(countries, countryOne); + + RedisFuture> countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + asyncCommands.sadd(countries, countryTwo); + countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + asyncCommands.sadd(countries, countryThree); + countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + RedisFuture exists = asyncCommands.sismember(countries, countryThree); + assertTrue(exists.get()); + } + + @Test + public void givenARanking_thenSaveItInRedisSortedSetAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String key = "sortedset"; + + asyncCommands.zadd(key, 1, "one"); + asyncCommands.zadd(key, 4, "zero"); + asyncCommands.zadd(key, 2, "two"); + + RedisFuture> values = asyncCommands.zrevrange(key, 0, 3); + Assert.assertEquals("zero", values.get().get(0)); + + values = asyncCommands.zrange(key, 0, 3); + Assert.assertEquals("one", values.get().get(0)); + } + + @Test + public void givenMultipleOperationsThatNeedToBeExecutedAtomically_thenWrapThemInATransaction() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + // Start a transaction + asyncCommands.multi(); + + // Add three sets to it, and save the future responses + RedisFuture result1 = asyncCommands.set("key1", "value1"); + RedisFuture result2 = asyncCommands.set("key2", "value2"); + RedisFuture result3 = asyncCommands.set("key3", "value3"); + + // Execute it + RedisFuture execResult = asyncCommands.exec(); + + TransactionResult transactionResult = execResult.get(); + + // Get the three results in the transaction return + String firstResult = transactionResult.get(0); + String secondResult = transactionResult.get(0); + String thirdResult = transactionResult.get(0); + + // Our results are in both! + assertTrue(firstResult.equals("OK")); + assertTrue(secondResult.equals("OK")); + assertTrue(thirdResult.equals("OK")); + + assertTrue(result1.get().equals("OK")); + assertTrue(result2.get().equals("OK")); + assertTrue(result3.get().equals("OK")); + } + + @Test + public void givenMultipleIndependentOperations_whenNetworkOptimizationIsImportant_thenFlushManually() throws Exception { + + int iterations = 50; + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + asyncCommands.setAutoFlushCommands(false); + + List> futures = new ArrayList<>(); + for (int i = 0; i < iterations; i++) { + futures.add(asyncCommands.set("key" + i, "value" + i)); + } + + asyncCommands.flushCommands(); + + // Wait until all futures complete + boolean result = LettuceFutures.awaitAll(5, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()])); + + asyncCommands.setAutoFlushCommands(true); + + } + + @Test + public void givenPubSubChannel_whenMessage_thenMessageReceived() throws Exception { + + Listener listener = new Listener(); + StatefulRedisPubSubConnection connection = redisClient.connectPubSub(); + StatefulRedisPubSubConnection pubconnection = redisClient.connectPubSub(); + connection.addListener(listener); + + RedisPubSubAsyncCommands async = connection.async(); + async.subscribe("channel"); + + RedisPubSubAsyncCommands pubasync = pubconnection.async(); + RedisFuture result = pubasync.publish("channel", "hithere"); + + // Need a long wait for publish to complete, depending on system. + result.get(15, TimeUnit.SECONDS); + assertTrue(listener.getMessage().equals("hithere")); + + } + + private static class Listener implements RedisPubSubListener { + + private String message; + + String getMessage() { + return message; + } + + @Override + public void message(String channel, String message) { + log.debug("Got {} on {}", message, channel); + this.message = message; + } + + @Override + public void message(String pattern, String channel, String message) { + + } + + @Override + public void subscribed(String channel, long count) { + log.debug("Subscribed to {}", channel); + } + + @Override + public void psubscribed(String pattern, long count) { + + } + + @Override + public void unsubscribed(String channel, long count) { + + } + + @Override + public void punsubscribed(String pattern, long count) { + + } + } + +} From a758fd9cef9fb4ad945d623eab443e0bd1bfd901 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Sun, 14 Jan 2018 22:50:55 +0100 Subject: [PATCH 012/118] BAEL-1402 code for the article: Try-with-resources in Kotlin --- core-kotlin/pom.xml | 5 ++ .../kotlin/com/baeldung/kotlin/UseTest.kt | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml index b511f0dd7b..2cd5275eeb 100644 --- a/core-kotlin/pom.xml +++ b/core-kotlin/pom.xml @@ -44,6 +44,11 @@ kotlin-stdlib ${kotlin-stdlib.version} + + org.jetbrains.kotlin + kotlin-stdlib-jre8 + ${kotlin-stdlib.version} + org.jetbrains.kotlin kotlin-test-junit diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt new file mode 100644 index 0000000000..15bdfcafd8 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt @@ -0,0 +1,67 @@ +package com.baeldung.kotlin + +import org.junit.Test +import java.beans.ExceptionListener +import java.beans.XMLEncoder +import java.io.* +import java.lang.Exception +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail + +class UseTest { + + @Test + fun givenCloseable_whenUseIsCalled_thenItIsClosed() { + val stringWriter = StringWriter() + val writer = BufferedWriter(stringWriter) //Using a BufferedWriter because after close() it throws. + writer.use { + assertEquals(writer, it) + + it.write("something") + } + try { + writer.write("something else") + + fail("write() should have thrown an exception because the writer is closed.") + } catch (e: IOException) { + //Ok + } + + assertEquals("something", stringWriter.toString()) + } + + @Test + fun givenAutoCloseable_whenUseIsCalled_thenItIsClosed() { + val baos = ByteArrayOutputStream() + val encoder = XMLEncoder(PrintStream(baos)) //XMLEncoder is AutoCloseable but not Closeable. + //Here, we use a PrintStream because after close() it throws. + encoder.exceptionListener = ThrowingExceptionListener() + encoder.use { + assertEquals(encoder, it) + + it.writeObject("something") + } + try { + encoder.writeObject("something else") + encoder.flush() + + fail("write() should have thrown an exception because the encoder is closed.") + } catch (e: IOException) { + //Ok + } + } + + @Test + fun whenSimpleFormIsUsed_thenItWorks() { + StringWriter().use { it.write("something") } + } +} + +class ThrowingExceptionListener : ExceptionListener { + override fun exceptionThrown(e: Exception?) { + if(e != null) { + throw e + } + } +} \ No newline at end of file From ee3c3a6237dfdc488dbca0d0e33fc6749d9530fc Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Mon, 15 Jan 2018 01:32:47 +0100 Subject: [PATCH 013/118] BAEL-1402 code for the article: Try-with-resources in Kotlin (#3418) --- core-kotlin/pom.xml | 5 ++ .../kotlin/com/baeldung/kotlin/UseTest.kt | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml index b511f0dd7b..2cd5275eeb 100644 --- a/core-kotlin/pom.xml +++ b/core-kotlin/pom.xml @@ -44,6 +44,11 @@ kotlin-stdlib ${kotlin-stdlib.version} + + org.jetbrains.kotlin + kotlin-stdlib-jre8 + ${kotlin-stdlib.version} + org.jetbrains.kotlin kotlin-test-junit diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt new file mode 100644 index 0000000000..15bdfcafd8 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt @@ -0,0 +1,67 @@ +package com.baeldung.kotlin + +import org.junit.Test +import java.beans.ExceptionListener +import java.beans.XMLEncoder +import java.io.* +import java.lang.Exception +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail + +class UseTest { + + @Test + fun givenCloseable_whenUseIsCalled_thenItIsClosed() { + val stringWriter = StringWriter() + val writer = BufferedWriter(stringWriter) //Using a BufferedWriter because after close() it throws. + writer.use { + assertEquals(writer, it) + + it.write("something") + } + try { + writer.write("something else") + + fail("write() should have thrown an exception because the writer is closed.") + } catch (e: IOException) { + //Ok + } + + assertEquals("something", stringWriter.toString()) + } + + @Test + fun givenAutoCloseable_whenUseIsCalled_thenItIsClosed() { + val baos = ByteArrayOutputStream() + val encoder = XMLEncoder(PrintStream(baos)) //XMLEncoder is AutoCloseable but not Closeable. + //Here, we use a PrintStream because after close() it throws. + encoder.exceptionListener = ThrowingExceptionListener() + encoder.use { + assertEquals(encoder, it) + + it.writeObject("something") + } + try { + encoder.writeObject("something else") + encoder.flush() + + fail("write() should have thrown an exception because the encoder is closed.") + } catch (e: IOException) { + //Ok + } + } + + @Test + fun whenSimpleFormIsUsed_thenItWorks() { + StringWriter().use { it.write("something") } + } +} + +class ThrowingExceptionListener : ExceptionListener { + override fun exceptionThrown(e: Exception?) { + if(e != null) { + throw e + } + } +} \ No newline at end of file From 11bcac71591140d2288abb19af873b2e4c2db791 Mon Sep 17 00:00:00 2001 From: Thoughtscript Date: Mon, 15 Jan 2018 05:01:38 +0000 Subject: [PATCH 014/118] BAEL-1175 - Finalized example and improved scripts --- .../bash/hadoop.sh | 6 +- .../boot/.gitignore | 3 + .../spring-cloud-stream-starters/boot/pom.xml | 55 +++++++++++++++++++ .../twitterhdfs/aggregate/AggregateApp.java | 18 ++++++ .../twitterhdfs/processor/ProcessorApp.java | 20 +++++++ .../baeldung/twitterhdfs/sink/SinkApp.java | 22 ++++++++ .../twitterhdfs/source/SourceApp.java | 26 +++++++++ .../src/main/resources/application.properties | 6 ++ .../spring-cloud-stream-starters/hdfs/hdfs.sh | 2 +- .../twitter/application.properties | 8 +-- .../twitter/twitter.sh | 2 +- 11 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/.gitignore create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/pom.xml create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java create mode 100644 spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties diff --git a/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh index 5eebcca426..ca8298430b 100644 --- a/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh +++ b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # For Ubuntu 14.04 # Inspired from: https://github.com/curran/setupHadoop/blob/master/setupHadoop.sh # Use from the user directory @@ -32,7 +34,7 @@ sudo mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys # SSH ssh localhost -# Authenticate with local user +# authenticate with osboxes.org # Start NameNode daemon and DataNode daemon start-dfs.sh @@ -40,3 +42,5 @@ start-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/hdfs.sh b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh index a9df476ef4..a6e6678feb 100644 --- a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -5,7 +5,7 @@ git clone https://github.com/spring-cloud-stream-app-starters/hdfs.git # Build it ./mvnw clean install -PgenerateApps -# RUn it +# Run it cd apps # Optionally inject application.properties prior to build java -jar hdfs-sink.jar --fsUri=http://osboxes:50075 \ 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 index 5cfedee80e..e38612d25e 100644 --- a/spring-cloud/spring-cloud-stream-starters/twitter/application.properties +++ b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties @@ -1,4 +1,4 @@ -twitter.credentials.access-token=932486336086286336-2HURQbA2cYzX5hixgAshIBy2Dhefupn -twitter.credentials.access-token-secret=0pyZ7etHvro8x85QSXxsqYFzYk63bK6DS5nNYy0R3l1io -twitter.credentials.consumer-key=10xCXkRYi5xLFYq3P0ymWGEwJ -twitter.credentials.consumer-secret=VfyCUcGfAQ2aWcd3uTg8GmVGyhUfAcNJU6ksG09TAtPMqhmWTS \ No newline at end of file +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 index 994d40dd4c..4c76fe637b 100644 --- a/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -5,7 +5,7 @@ git clone https://github.com/spring-cloud-stream-app-starters/twitter.git # Build it ./mvnw clean install -PgenerateApps -# RUn it +# Run it cd apps # Optionally inject application.properties prior to build java -jar twitter_stream_source.jar --consumerKey= --consumerSecret= \ From 68b4eddc977f3d7ad85c885d81f550ad842f53a7 Mon Sep 17 00:00:00 2001 From: Thoughtscript Date: Mon, 15 Jan 2018 05:05:00 +0000 Subject: [PATCH 015/118] BAEL-1175 - Corrected HDFS URI --- .../spring-cloud-stream-starters/hdfs/application.properties | 2 +- spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties index 8b421f954c..1f4aaf88dd 100644 --- a/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties @@ -1 +1 @@ -hdfs.fs-uri=http://osboxes:50075 \ No newline at end of file +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 index a6e6678feb..577a25dd6e 100644 --- a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -8,4 +8,4 @@ git clone https://github.com/spring-cloud-stream-app-starters/hdfs.git # Run it cd apps # Optionally inject application.properties prior to build -java -jar hdfs-sink.jar --fsUri=http://osboxes:50075 \ No newline at end of file +java -jar hdfs-sink.jar --fsUri=hdfs://127.0.0.1:50010/ \ No newline at end of file From 7e5940b578b9d516dcb8d4fb714c72c4fb1ebf44 Mon Sep 17 00:00:00 2001 From: Tarang Bhalodia Date: Tue, 16 Jan 2018 01:11:38 +0530 Subject: [PATCH 016/118] tarangbhalodia@gmail.com [BAEL-1282: geospatial support elasticsearch] (#3421) * BAEL-1422: measure performance of Random and ThreadLocalRandom using JMH * BAEL-1422: updated benchmarking examples of Random and ThreadLocalRandom to use newWorkStealingPool that leverages ForkJoinPool * BAEL-1422: refactored benchmarking examples for comparing performance of ThreadLocalRandom and Random - initialised the collection of Callable before running benchmarking - removed for loop for submitting task and instead used executor.invokeAll(collection_of_callable) * BAEL-1282: added TDD type junit tests for geospatial queries elasticsearch --- spring-data-elasticsearch/pom.xml | 18 ++ .../elasticsearch/GeoQueriesTest.java | 179 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java diff --git a/spring-data-elasticsearch/pom.xml b/spring-data-elasticsearch/pom.xml index 520707a432..688506450f 100644 --- a/spring-data-elasticsearch/pom.xml +++ b/spring-data-elasticsearch/pom.xml @@ -45,6 +45,24 @@ ${spring-data-elasticsearch.version} + + com.spatial4j + spatial4j + 0.4.1 + + + + com.vividsolutions + jts + 1.13 + + + xerces + xercesImpl + + + + org.springframework spring-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 new file mode 100644 index 0000000000..19514ce4c2 --- /dev/null +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java @@ -0,0 +1,179 @@ +package com.baeldung.elasticsearch; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.geo.ShapeRelation; +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.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@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"; + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; + + @Autowired + private Client client; + + @Before + public void setUp() { + String jsonObject = "{\"Wonders\":{\"properties\":{\"name\":{\"type\":\"string\",\"index\":\"not_analyzed\"},\"region\":{\"type\":\"geo_shape\",\"tree\":\"quadtree\",\"precision\":\"1m\"},\"location\":{\"type\":\"geo_point\"}}}}"; + CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); + req.mapping(WONDERS, jsonObject); + client.admin() + .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(); + String tajMahalId = response.getId(); + client.admin() + .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); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(tajMahalId)); + } + + @Test + 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(); + String pyramidsOfGizaId = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location") + .bottomLeft(28, 30) + .topRight(31, 32); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(pyramidsOfGizaId)); + } + + @Test + 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(); + String lighthouseOfAlexandriaId = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") + .point(29.976, 31.131) + .distance(10, DistanceUnit.MILES); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(lighthouseOfAlexandriaId)); + } + + @Test + 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(); + String greatRannOfKutchid = response.getId(); + client.admin() + .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); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(greatRannOfKutchid)); + } + + @After + public void destroy() { + elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD); + } + +} From 293968321eb980e6599dd4f81b002b85e64308ad Mon Sep 17 00:00:00 2001 From: Antonio David Perez Morales Date: Mon, 15 Jan 2018 21:00:21 +0100 Subject: [PATCH 017/118] BAEL-1428: Adding example for manually set authenticated user (#3423) --- .../web/controller/LoginController.java | 43 ++++++++++++++ .../web/controller/PrintUserController.java | 27 +++++++++ .../security/spring/ManualSecurityConfig.java | 58 +++++++++++++++++++ .../spring/ManualSecurityIntegrationTest.java | 58 +++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java create mode 100644 spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java create mode 100644 spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java create mode 100644 spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java new file mode 100644 index 0000000000..c67a6f667e --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java @@ -0,0 +1,43 @@ +package org.baeldung.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +@RequestMapping(value = "/custom") +public class LoginController { + + @Autowired + private AuthenticationManager authManager; + + public LoginController() { + super(); + } + + // API + + // custom login + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public void login(@RequestParam("username") final String username, @RequestParam("password") final String password, final HttpServletRequest request) { + UsernamePasswordAuthenticationToken authReq = + new UsernamePasswordAuthenticationToken(username, password); + Authentication auth = authManager.authenticate(authReq); + SecurityContext sc = SecurityContextHolder.getContext(); + sc.setAuthentication(auth); + HttpSession session = request.getSession(true); + session.setAttribute("SPRING_SECURITY_CONTEXT", sc); + } + +} \ No newline at end of file diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java new file mode 100644 index 0000000000..78f164c7f1 --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java @@ -0,0 +1,27 @@ +package org.baeldung.web.controller; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +@RequestMapping(value = "/custom") +public class PrintUserController { + + public PrintUserController() { + super(); + } + + // API + + // print user + + @RequestMapping(value = "/print", method = RequestMethod.GET) + public void printUser() { + SecurityContext sc = SecurityContextHolder.getContext(); + System.out.println("Logged User: "+sc.getAuthentication().getName()); + } + +} \ No newline at end of file diff --git a/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java new file mode 100644 index 0000000000..874856095c --- /dev/null +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java @@ -0,0 +1,58 @@ +package org.baeldung.security.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.BeanIds; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class ManualSecurityConfig extends WebSecurityConfigurerAdapter { + + public ManualSecurityConfig() { + super(); + } + + // java config + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("user1Pass").authorities("ROLE_USER").and().withUser("admin").password("adminPass").authorities("ROLE_ADMIN"); + } + + @Override + public void configure(final WebSecurity web) throws Exception { + web.ignoring().antMatchers("/resources/**"); + } + + @Bean(name = BeanIds.AUTHENTICATION_MANAGER) + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(final HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .mvcMatchers("/custom/login").permitAll() + .anyRequest().authenticated() + .and() + .httpBasic() + .and() + .headers().cacheControl().disable() + .and() + .csrf().disable() + ; + // @formatter:on + } + +} diff --git a/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java new file mode 100644 index 0000000000..afc86bd74c --- /dev/null +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java @@ -0,0 +1,58 @@ +package org.baeldung.security.spring; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.servlet.http.HttpSession; + +import org.baeldung.spring.MvcConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { MvcConfig.class, ManualSecurityConfig.class }) +public class ManualSecurityIntegrationTest { + + @Autowired + WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(SecurityMockMvcConfigurers.springSecurity()).build(); + } + + /** + * Execute custom login and access the endpoint + */ + @Test + public void whenLoginIsSuccessFulThenEndpointCanBeAccessedAndCurrentUserPrinted() throws Exception { + + mockMvc.perform(get("/custom/print")) + .andExpect(status().isUnauthorized()); + + HttpSession session = mockMvc.perform(post("/custom/login").param("username", "user1").param("password", "user1Pass")) + .andExpect(status().isOk()) + .andReturn() + .getRequest() + .getSession(); + + mockMvc.perform(get("/custom/print").session((MockHttpSession) session)) + .andExpect(status().is2xxSuccessful()); + } + +} From f993bc0435953a345abd9047a64bd8a047f0c23b Mon Sep 17 00:00:00 2001 From: Bogdan Stoean <4540392+BogdanStoean@users.noreply.github.com> Date: Mon, 15 Jan 2018 23:05:19 +0200 Subject: [PATCH 018/118] [BAEL-1410] Spring Boot OAuth2 Support (#3409) * initial setup with spring boot/ spring data jpa/ flyway * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - created multiple migration scripts and locations * BAEL-1315 - test insert after schema creation * cleanup * BAEL-1315 - test data changes by a migration * [BAEL-1410] Spring Boot Security Auto-Configuration * [BAEL-1410] Added some tests for incorrect credentials use case * [BAEL-1410] Added readme and some code improvements * [BAEL-1410] removed form based auth config because is redundant added oauth2 server auto-configuration sample with test * [BAEL-1410] added custom Authorization Server Config * [BAEL-1410] update README * [BAEL-1410]refactor tests * [BAEL-1410]oauth2 resource server * [BAEL-1410]oauth2 sso sample with facebook * [BAEL-1410]remove spring-flyway --- spring-boot-security/README.md | 8 +- spring-boot-security/pom.xml | 4 + .../SpringBootSecurityApplication.java | 4 +- .../config/BasicAuthConfiguration.java} | 6 +- .../config/FormLoginConfiguration.java | 39 ------- .../SpringBootOAuth2ResourceApplication.java | 30 +++++ ...ingBootAuthorizationServerApplication.java | 30 +++++ .../config/AuthorizationServerConfig.java | 39 +++++++ .../SpringBootOAuth2SsoApplication.java | 18 +++ .../resources/application-authz.properties | 3 + .../resources/application-resource.properties | 2 + .../main/resources/application-sso.properties | 9 ++ .../src/main/resources/application.properties | 6 +- .../FormConfigurationIntegrationTest.java | 106 ------------------ ...asicAuthConfigurationIntegrationTest.java} | 9 +- ...figAuthorizationServerIntegrationTest.java | 75 +++++++++++++ ...figAuthorizationServerIntegrationTest.java | 44 ++++++++ 17 files changed, 270 insertions(+), 162 deletions(-) rename spring-boot-security/src/main/java/com/baeldung/springbootsecurity/{ => basic_auth}/SpringBootSecurityApplication.java (80%) rename spring-boot-security/src/main/java/com/baeldung/springbootsecurity/{config/BasicConfiguration.java => basic_auth/config/BasicAuthConfiguration.java} (84%) delete mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java create mode 100644 spring-boot-security/src/main/resources/application-authz.properties create mode 100644 spring-boot-security/src/main/resources/application-resource.properties create mode 100644 spring-boot-security/src/main/resources/application-sso.properties delete mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java rename spring-boot-security/src/test/java/com/baeldung/springbootsecurity/{BasicConfigurationIntegrationTest.java => basic_auth/BasicAuthConfigurationIntegrationTest.java} (86%) create mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java create mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java diff --git a/spring-boot-security/README.md b/spring-boot-security/README.md index 26ab8b2337..a0ddb8de7b 100644 --- a/spring-boot-security/README.md +++ b/spring-boot-security/README.md @@ -1,6 +1,8 @@ ### Spring Boot Security Auto-Configuration - mvn clean install -- uncomment in application.properties spring.profiles.active=basic # for basic auth config -- uncomment in application.properties spring.profiles.active=form # for form based auth config -- uncomment actuator dependency simultaneously with the line from main class +- uncomment actuator dependency simultaneously with the line from basic auth main class +- uncomment security properties for easy testing. If not random will be generated. + +### CURL commands +- curl -X POST -u baeldung-admin:baeldung -d grant_type=client_credentials -d username=baeldung-admin -d password=baeldung http://localhost:8080/oauth/token diff --git a/spring-boot-security/pom.xml b/spring-boot-security/pom.xml index c35191a7fc..c1ec14ff64 100644 --- a/spring-boot-security/pom.xml +++ b/spring-boot-security/pom.xml @@ -43,6 +43,10 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.security.oauth + spring-security-oauth2 + org.springframework.boot spring-boot-starter-web diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java similarity index 80% rename from spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java rename to spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java index 3a85da72e5..2ecad4ae35 100644 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java @@ -1,4 +1,4 @@ -package com.baeldung.springbootsecurity; +package com.baeldung.springbootsecurity.basic_auth; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -7,7 +7,7 @@ import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration @SpringBootApplication(exclude = { SecurityAutoConfiguration.class // ,ManagementWebSecurityAutoConfiguration.class -}) +}, scanBasePackages = "com.baeldung.springbootsecurity.basic_auth") public class SpringBootSecurityApplication { public static void main(String[] args) { diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java similarity index 84% rename from spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java rename to spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java index 1b08e5ee22..993c573fb0 100644 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java @@ -1,7 +1,6 @@ -package com.baeldung.springbootsecurity.config; +package com.baeldung.springbootsecurity.basic_auth.config; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; 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; @@ -9,8 +8,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @Configuration @EnableWebSecurity -@Profile("basic") -public class BasicConfiguration extends WebSecurityConfigurerAdapter { +public class BasicAuthConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java deleted file mode 100644 index b4902a9ffc..0000000000 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.baeldung.springbootsecurity.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -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; - -@Configuration -@EnableWebSecurity -@Profile("form") -public class FormLoginConfiguration extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user") - .password("password") - .roles("USER") - .and() - .withUser("admin") - .password("password") - .roles("USER", "ADMIN"); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest() - .authenticated() - .and() - .formLogin() - .and() - .httpBasic(); - } -} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java new file mode 100644 index 0000000000..56231a28bd --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java @@ -0,0 +1,30 @@ +package com.baeldung.springbootsecurity.oauth2resource; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@EnableResourceServer +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2resource") +public class SpringBootOAuth2ResourceApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .profiles("resource") + .sources(SpringBootOAuth2ResourceApplication.class) + .build() + .run(args); + } + + @RestController + class SecuredResourceController { + + @GetMapping("/securedResource") + public String securedResource() { + return "Baeldung Secured Resource OK"; + } + + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java new file mode 100644 index 0000000000..44dabefbb8 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java @@ -0,0 +1,30 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +@EnableResourceServer +@EnableAuthorizationServer +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2server") +public class SpringBootAuthorizationServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootAuthorizationServerApplication.class, args); + } + + @RestController + class UserController { + + @GetMapping("/user") + public Principal user(Principal user) { + return user; + } + + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java new file mode 100644 index 0000000000..b403feb5c1 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java @@ -0,0 +1,39 @@ +package com.baeldung.springbootsecurity.oauth2server.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationManager; +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.configurers.AuthorizationServerEndpointsConfigurer; + +@Configuration +@Profile("authz") +public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + + @Autowired + private AuthenticationManager authenticationManager; + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.authenticationManager(authenticationManager); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients + .inMemory() + .withClient("baeldung") + .secret("baeldung") + .authorizedGrantTypes("client_credentials", "password", "authorization_code") + .scopes("openid", "read") + .autoApprove(true) + .and() + .withClient("baeldung-admin") + .secret("baeldung") + .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token") + .scopes("read", "write") + .autoApprove(true); + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java new file mode 100644 index 0000000000..b1cd580f08 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java @@ -0,0 +1,18 @@ +package com.baeldung.springbootsecurity.oauth2sso; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@EnableOAuth2Sso +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2sso") +public class SpringBootOAuth2SsoApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .profiles("sso") + .sources(SpringBootOAuth2SsoApplication.class) + .build() + .run(args); + } +} diff --git a/spring-boot-security/src/main/resources/application-authz.properties b/spring-boot-security/src/main/resources/application-authz.properties new file mode 100644 index 0000000000..d29b0cdd3c --- /dev/null +++ b/spring-boot-security/src/main/resources/application-authz.properties @@ -0,0 +1,3 @@ +security.user.password=password +security.oauth2.client.client-id=client +security.oauth2.client.client-secret=secret diff --git a/spring-boot-security/src/main/resources/application-resource.properties b/spring-boot-security/src/main/resources/application-resource.properties new file mode 100644 index 0000000000..b157b01d51 --- /dev/null +++ b/spring-boot-security/src/main/resources/application-resource.properties @@ -0,0 +1,2 @@ +server.port=8081 +security.oauth2.resource.userInfoUri=http://localhost:8080/user \ No newline at end of file diff --git a/spring-boot-security/src/main/resources/application-sso.properties b/spring-boot-security/src/main/resources/application-sso.properties new file mode 100644 index 0000000000..ac6ae0cc93 --- /dev/null +++ b/spring-boot-security/src/main/resources/application-sso.properties @@ -0,0 +1,9 @@ +server.port=8082 +security.oauth2.client.clientId= +security.oauth2.client.clientSecret= +security.oauth2.client.accessTokenUri=https://graph.facebook.com/oauth/access_token +security.oauth2.client.userAuthorizationUri=https://www.facebook.com/dialog/oauth +security.oauth2.client.tokenName=oauth_token +security.oauth2.client.authenticationScheme=query +security.oauth2.client.clientAuthenticationScheme=form +security.oauth2.resource.userInfoUri=https://graph.facebook.com/me \ No newline at end of file diff --git a/spring-boot-security/src/main/resources/application.properties b/spring-boot-security/src/main/resources/application.properties index 6ca2edb175..c2b8d70dc6 100644 --- a/spring-boot-security/src/main/resources/application.properties +++ b/spring-boot-security/src/main/resources/application.properties @@ -1,4 +1,4 @@ #spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration -#spring.profiles.active=form -#spring.profiles.active=basic -#security.user.password=password \ No newline at end of file +#security.user.password=password +#security.oauth2.client.client-id=client +#security.oauth2.client.client-secret=secret diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java deleted file mode 100644 index 697a4f2868..0000000000 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.baeldung.springbootsecurity; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.embedded.LocalServerPort; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -import java.util.Collections; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.junit.Assert.*; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) -@ActiveProfiles("form") -public class FormConfigurationIntegrationTest { - - @Autowired TestRestTemplate restTemplate; - @LocalServerPort int port; - - @Test - public void whenLoginPageIsRequested_ThenSuccess() { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - ResponseEntity responseEntity = restTemplate.exchange("/login", HttpMethod.GET, new HttpEntity(httpHeaders), String.class); - assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); - assertTrue(responseEntity - .getBody() - .contains("_csrf")); - } - - @Test - public void whenTryingToLoginWithCorrectCredentials_ThenAuthenticateWithSuccess() { - HttpHeaders httpHeaders = getHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap form = getFormSubmitCorrectCredentials(); - ResponseEntity responseEntity = this.restTemplate.exchange("/login", HttpMethod.POST, new HttpEntity<>(form, httpHeaders), String.class); - assertEquals(responseEntity.getStatusCode(), HttpStatus.FOUND); - assertTrue(responseEntity - .getHeaders() - .getLocation() - .toString() - .endsWith(this.port + "/")); - assertNotNull(responseEntity - .getHeaders() - .get("Set-Cookie")); - } - - @Test - public void whenTryingToLoginWithInorrectCredentials_ThenAuthenticationFailed() { - HttpHeaders httpHeaders = getHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap form = getFormSubmitIncorrectCredentials(); - ResponseEntity responseEntity = this.restTemplate.exchange("/login", HttpMethod.POST, new HttpEntity<>(form, httpHeaders), String.class); - assertEquals(responseEntity.getStatusCode(), HttpStatus.FOUND); - assertTrue(responseEntity - .getHeaders() - .getLocation() - .toString() - .endsWith(this.port + "/login?error")); - assertNull(responseEntity - .getHeaders() - .get("Set-Cookie")); - } - - private MultiValueMap getFormSubmitCorrectCredentials() { - MultiValueMap form = new LinkedMultiValueMap<>(); - form.set("username", "user"); - form.set("password", "password"); - return form; - } - - private MultiValueMap getFormSubmitIncorrectCredentials() { - MultiValueMap form = new LinkedMultiValueMap<>(); - form.set("username", "user"); - form.set("password", "wrongpassword"); - return form; - } - - private HttpHeaders getHeaders() { - HttpHeaders headers = new HttpHeaders(); - ResponseEntity page = this.restTemplate.getForEntity("/login", String.class); - assertEquals(page.getStatusCode(), HttpStatus.OK); - String cookie = page - .getHeaders() - .getFirst("Set-Cookie"); - headers.set("Cookie", cookie); - Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*"); - Matcher matcher = pattern.matcher(page.getBody()); - assertTrue(matcher.matches()); - headers.set("X-CSRF-TOKEN", matcher.group(1)); - return headers; - } - -} diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java similarity index 86% rename from spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java rename to spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java index 63e1c2ac73..4e4244abb7 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java @@ -1,5 +1,6 @@ -package com.baeldung.springbootsecurity; +package com.baeldung.springbootsecurity.basic_auth; +import com.baeldung.springbootsecurity.basic_auth.SpringBootSecurityApplication; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -8,7 +9,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @@ -20,9 +20,8 @@ import static org.junit.Assert.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) -@ActiveProfiles("basic") -public class BasicConfigurationIntegrationTest { +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootSecurityApplication.class) +public class BasicAuthConfigurationIntegrationTest { TestRestTemplate restTemplate; URL base; diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java new file mode 100644 index 0000000000..09df9ce645 --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java @@ -0,0 +1,75 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class) +@ActiveProfiles("authz") +public class CustomConfigAuthorizationServerIntegrationTest { + + @Value("${local.server.port}") protected int port; + + @Test + public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("read")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + + } + + @Test(expected = OAuth2AccessDeniedException.class) + public void whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + restTemplate.getAccessToken(); + } + + @Test + public void whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung-admin"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + } + + private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setGrantType("client_credentials"); + return resourceDetails; + } + +} + diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java new file mode 100644 index 0000000000..c7b1b4ef6c --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java @@ -0,0 +1,44 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.test.context.junit4.SpringRunner; + +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class, + properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=secret" }) +public class DefaultConfigAuthorizationServerIntegrationTest { + + @Value("${local.server.port}") protected int port; + + @Test + public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setClientId("client"); + resourceDetails.setClientSecret("secret"); + resourceDetails.setGrantType("client_credentials"); + resourceDetails.setScope(asList("read", "write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + + } + +} + From c12ba1bf26907c66b3a38a2f883c7e42218172f6 Mon Sep 17 00:00:00 2001 From: abirkhan04 Date: Tue, 16 Jan 2018 22:02:54 +0600 Subject: [PATCH 019/118] Spring-cloud-security project is added here. --- spring-cloud-security/README.md | 29 +++++ spring-cloud-security/alias.rtf | 28 +++++ spring-cloud-security/authserver/pom.xml | 42 ++++++++ .../com/cloudsecurity/auth/AuthServer.java | 15 +++ .../auth/config/AuthServerConfigurer.java | 77 +++++++++++++ .../auth/config/ResourceServerConfigurer.java | 24 +++++ .../auth/config/WebMvcConfigurer.java | 15 +++ .../auth/config/WebSecurityConfigurer.java | 56 ++++++++++ .../auth/controller/ResourceController.java | 20 ++++ .../src/main/resources/application.yml | 21 ++++ .../main/resources/certificate/mykeystore.jks | Bin 0 -> 2255 bytes .../src/main/resources/templates/login.html | 29 +++++ spring-cloud-security/mykeystore.jks | Bin 0 -> 2255 bytes spring-cloud-security/personservice.zip | Bin 0 -> 50761 bytes spring-cloud-security/personservice/pom.xml | 74 +++++++++++++ .../com/baeldung/service/model/Person.java | 51 +++++++++ .../PersonserviceApplication.java | 12 +++ .../config/ResourceConfigurer.java | 25 +++++ .../controller/PersonInfoController.java | 31 ++++++ .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 25 +++++ .../PersonserviceApplicationTests.java | 16 +++ spring-cloud-security/pubkey.txt | 30 ++++++ .../springoath2client/pom.xml | 102 ++++++++++++++++++ .../com/cloud/springwebsite/CloudSite.java | 24 +++++ .../config/SiteSecurityConfigurer.java | 49 +++++++++ .../controller/CloudSiteController.java | 39 +++++++ .../cloudsite/filters/pre/SimpleFilter.java | 39 +++++++ .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 37 +++++++ .../src/main/resources/static/index.html | 60 +++++++++++ .../main/resources/templates/personinfo.html | 32 ++++++ .../Springoath2ApplicationTests.java | 16 +++ 33 files changed, 1018 insertions(+) create mode 100644 spring-cloud-security/README.md create mode 100644 spring-cloud-security/alias.rtf create mode 100644 spring-cloud-security/authserver/pom.xml create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java create mode 100644 spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java create mode 100644 spring-cloud-security/authserver/src/main/resources/application.yml create mode 100644 spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks create mode 100644 spring-cloud-security/authserver/src/main/resources/templates/login.html create mode 100644 spring-cloud-security/mykeystore.jks create mode 100644 spring-cloud-security/personservice.zip create mode 100644 spring-cloud-security/personservice/pom.xml create mode 100644 spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java create mode 100644 spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java create mode 100644 spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java create mode 100644 spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java create mode 100644 spring-cloud-security/personservice/src/main/resources/application.properties create mode 100644 spring-cloud-security/personservice/src/main/resources/application.yml create mode 100644 spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java create mode 100644 spring-cloud-security/pubkey.txt create mode 100644 spring-cloud-security/springoath2client/pom.xml create mode 100644 spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java create mode 100644 spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java create mode 100644 spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java create mode 100644 spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java create mode 100644 spring-cloud-security/springoath2client/src/main/resources/application.properties create mode 100644 spring-cloud-security/springoath2client/src/main/resources/application.yml create mode 100644 spring-cloud-security/springoath2client/src/main/resources/static/index.html create mode 100644 spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html create mode 100644 spring-cloud-security/springoath2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java diff --git a/spring-cloud-security/README.md b/spring-cloud-security/README.md new file mode 100644 index 0000000000..39af52c077 --- /dev/null +++ b/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-security/alias.rtf b/spring-cloud-security/alias.rtf new file mode 100644 index 0000000000..15509e1c83 --- /dev/null +++ b/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-security/authserver/pom.xml b/spring-cloud-security/authserver/pom.xml new file mode 100644 index 0000000000..ab30f3f2ec --- /dev/null +++ b/spring-cloud-security/authserver/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + com.baeldung + auth-server + 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-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java new file mode 100644 index 0000000000..33b2391437 --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java @@ -0,0 +1,15 @@ +package com.cloudsecurity.auth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.*; + + +@SpringBootApplication +public class AuthServer { + + public static void main(String[] args) { + // TODO Auto-generated method stub + SpringApplication.run( + AuthServer.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java new file mode 100644 index 0000000000..78bae59b7d --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java @@ -0,0 +1,77 @@ +package com.cloudsecurity.auth.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-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java new file mode 100644 index 0000000000..06a4679f8b --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java @@ -0,0 +1,24 @@ +package com.cloudsecurity.auth.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-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java new file mode 100644 index 0000000000..839908fcc2 --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java @@ -0,0 +1,15 @@ +package com.cloudsecurity.auth.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"); + } +} diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java new file mode 100644 index 0000000000..eb81e65196 --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java @@ -0,0 +1,56 @@ +package com.cloudsecurity.auth.config; + +import org.springframework.beans.factory.annotation.Autowired; +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.client.OAuth2ClientContext; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; + +@Configuration +@EnableWebSecurity +@EnableOAuth2Client +public class WebSecurityConfigurer + extends + WebSecurityConfigurerAdapter { + + @Autowired + private OAuth2ClientContext oauth2ClientContext; + + @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(); + } + + +} \ No newline at end of file diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java new file mode 100644 index 0000000000..684181fcbb --- /dev/null +++ b/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java @@ -0,0 +1,20 @@ +package com.cloudsecurity.auth.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-security/authserver/src/main/resources/application.yml b/spring-cloud-security/authserver/src/main/resources/application.yml new file mode 100644 index 0000000000..1dc63d3f0e --- /dev/null +++ b/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-security/authserver/src/main/resources/certificate/mykeystore.jks b/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..9cf25e32249812febce1d7d829885bb575018e1d GIT binary patch literal 2255 zcmc(g`8$*i7su~=hOwJ6b`uIyJgR%fHY9slk_jPMC(8^*vP?y*$;g&TwiFT-!&oMY z>{^gzO4=-GG1jp}Wo&s~J=gWV*Yh8|Kb#*vpP$b4J?A=SV|HT}007u#f&Y|9Xf%-; zMGgsw-ZZ|c2yr<85QITAfelC!A^H~r6ackgTR~6|5TFT6SGFInS~D}!mckURzCxe# zGjUqg$L;Vh<_+9z4BQ_jow{#7ocA?`e8C#7^-!@`xMzmKi7Ec(-ej$fqzHq%FS56@ z^t~rK3RPyK!yjw4X)-#QjoG^u4|32YjQnVo?%30Kt;B_%`cQt@P)@|mYHo;8iYsFK zxldHl`TNtlSP@%_5Ig*LSj;xnNwoX2qsfe|+}SreHX2`-R(42fSnplD9M2J)b`&+Y zymlDKIpWdB?l)zjxIw2fk^Q``qcPY9+?AeU3!t zvUGM&dI`_Ulhh-yO;>gX2cujHT`867Cikj_kk?HT9OqaK;?&_lU_>DcV4NelUp z9m0D*9OsycI1i@!#l$XWb=1qNXZxNeYwtn~HDL1H*saHCE;XssahYDvSn!~=UQvNN zziH>;PS}hzEI#LFPh{c2P-dm;^2(yRm$Q?kw|JPR6TKbgp}WLZeLzq$?_^6^BwgLt ztE`r_Io^Hms@J#xiGi)oi z@5LwnOkl?~X7RF|_h59Xn$5gTq zxk5Mb4ymikKKpSyN$gzY_Uu%ditcQEz;9diOMhn#6S{tS&18^F(@~q*GhK|G@(zEO zru`ZTsAf48^N$>!cu^kcj3-J@S+)f~bz#KMQQ98hYQ_w@YaKNG6>H1P$KEp6a35vW z_IZ78bo+8R@lADj1EI3At02$1lqFPnA)UN}8RO2D@W-0{S%m{vjuyX!#e3}(H4S`m zY4&_1)4w-+J<#&uB zUVZ#X<1@Z=2VHXFM%DdY4nl9|NSK{< z9;eVse&6Qa_3YCZ42z2>;R3_R&*UUNT6ucizH%Dz^yL}_QbhTT{veds zn0XgchSS%NG&)4{`t&O7A6|Mb<#2B>w2TFMGT5Z+}lnelj8%{s5mg+a>co4%>~ZOm8Z4 z)}WmgXPTVJHz%|W?cI8e{cahpnpZa&5|@{Em4H4>CYldqZJo8t&b=m*lg1%)%+e$6 zChK6qOPD14pxP=ss*)@{+7z{GP)1fj=l!rxr}+j6zjq-9P@$Vrr^AJV{yC0<$M)z! zh5!J+0@2`?AR3HR0EdAvn1Ey%Yz~q@h-k&z#PcOUP)rbjPGQ9$5d_>t0D*$x@mLhJ z^$!T6L@kIXNfe@gKqMI}35oxaLMTBiGBJdR-3lT9uqaB{f*3&Yr=BEXWgyfaC596J zuR1{qBVnbWEq^o|B|-`FBT~p=kx`JcoCMYYi^J(*b+Nh-{`e;1A)Wu>|DhuqxZ~fk zY<>+m4MYME4HSjZKoE%ERd~K^)#$^Dsa68&+iR&Ejt7-X7jH8B`tOx-eGbl>KbeIu zDmIWkJ!uvLlk)q81)85Z<&hoiv7&#;OND}r7StTRTwnIy<*Skf)k!SglQR}_W4hS%Gs8C#_hIgJeMB0 zOHtCDI>Ce_#*Z?ty7E4a+v|j)6|0IqhC_A1Nkj3rETvPgTT1oTbLvzxWEp1C$kT6> zSs$*iu9d0DH=E)rMJ9vT8-$<7KL@`M#XQ?~098dDt6{(1R>#HeWC9a%ypBY?Bn6PRL zO~-oNE?asiM^9I!8fe2>9Zje6+$Zm~FQ5~zAUVq3Wntr{gks5P?o%1dOD)rBE~*#Q zeyG`0oU(fVf-s8Hvre8C+tHc4V4F4Coy + + + + Baeldung Spring cloud Security + + + +

Login

+ + +
+
+

Username and Password:

+

+ + +

+

+ + +

+

+ +

+
+
+ + \ No newline at end of file diff --git a/spring-cloud-security/mykeystore.jks b/spring-cloud-security/mykeystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..9cf25e32249812febce1d7d829885bb575018e1d GIT binary patch literal 2255 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%0DD(x6LIVRq4Ib=H;7V(N<*2Hiz!CxZ zjjM!mM?{@52bvi0Xf>?9845~?-mW!)%mYmEd-oaoxMzShBUN|nKM)q(4~dp$%`ZLzE5CVzV~n=42H*P zLmn04He@?IXQ&09!s|ok7O8`T@h&?xL=#HPki>PmL~v?WHl2$0hJu9$tInVFXAt{ll!Adj|w*Ev>IVklBUT99|EjLnV0nbV)VFo(u(GfBqI4oMg{(d zjQ%Z=oc?CBIN3%HSpZ@5%Za918yXA+6b}*6f(kqARnVJMQkpUWNaiqFW1~5%#cTQz z=tH(APMBnV2)-|rab_pGcn!FnId$uLX6D`1)YbLr=j`k*(6K!oWKiADHWaBP5eq3z zmU^P(=8FhVoetGk(=#*!G3j5`bhB11*aJt2+~ zX9n36nykTl(h=GrAkN*KYST+PEjPk4iw0>wQdT1)j38MQ$zmW;iJoj^a7-8Bb@5an zFm7IRa&m`eya&4_xA%m^W1}uP>0-EhMn zUeFq%s!CZz^Lcwq1+_671jC+c6O&CMfL9Dhz}ki1*PhU=U2+^i;17K0X5v!KD7&Bjk2OHieSf7nSDZMX{p8Ei! z088XF$bb&FWxW{t>DgdWK6oQm7M3rNRi{v8rCIs(NYR!yOV)8y{^N3D+u$yVmH7)Y z#}^;ztB;etj9EzFnU^naTPLm<_F1_>!(-}JxBXX_j1h0`U5mvw=zF@q2*AS9B1`;J z#r1#xCjun@eF6kcOzj*^{x@mrN(tEv;mgL+-kpPFL9kc^iw24qKomiTP;3Zl70r)T z9R1rQ=D>nIbBiH0@O)BuOl6QWzuqFzA@We3NpdBWonMQ5_A zCM}0%_M1}tI1(zMD3&fbIKIJEMGtEKOc6w=PUywYm+Etb6|qS8sn8Mymh1?HUp$-T zhg=*+3I}%Wc9mYb!(M>*fUTy$IeLGA@Xt3uvyX^RU7nkw?mRsJnKA~Mw18vbjzAHB zyVxMy&6`CuC1Sf;ugVF#TZ7p;9qkQVN?)~4RPz%zAZC$WC`=6?$G!+Rxo{3*@WK_d zp+PB1bhsoa5*WV{-~ViDXa;#wK;|kp47w*ew6W#Vn)}s{BxAy9(T{*&HzbU;c7#ngC* z4$(uXdpv@6EU&L^FmOe_KiEqHmT++^w~($={^e$y$z$*Js=a+C6O*kc3I`0=`PlS6 zxR99JraytU2az_UST$OIWP>)SJ+klXtXo8DBiPvZkdc30@>Ymhy|yhT2+lEb11QXI znNdXDe9M)KK(+W!`BG~Y6(xN5nZQs zyrKF*7nc$v#VU_)1QF>f&oSQqBAIuJBTx1ViY6yh57>tp@2EZCD^?r>j! z+@F&gcqpn~K(CT%PIFVROtVd|*)0?E?z8g#g-uezbQ$P=&^DL_yx1vyAgz{!-lMOn zddSK?M7L_6d$t;p^(piWzVWXi$mNgM-R(bV6$tj9f{OCLA5?@4oJ=HaolI<9U+TntCEbrx=mC2RKi8xTY#idw^>KInJ zp_+nVg!Z!J+E1KV9(b{HBLxoeC+m>B$l@QmE8Rmbdz2Y18P<|5a|ssVWQE~aQ?%LJ zM(eC2@T5gY1@Z2T^yq(d_WQs2)D+h%nUQiP5)2wV*}gDiWC>U+L|;ihMQ9+cXFVx% zaU|89>mO|pCupHrxsS<9GQ^M5cNysnN$6=l!1IKVG1% z-CZ!3Jv^fTx!BT=yDZ_jVPL_~Z6p8rO{ms!m8tqS>?e{?4Vv@=e^;t$kd+gEX9*MG zcB0U5VYo*B=k9CoUCr+kv4w*CHk@LB?Hf0Y$Z6h8-J9PQI_qaYoLyL^j9I{E0}SyeSfljRDLrNd-*Ak&h!_5!JuQsFTb@ z{g^w2DKj8`V`)H-&CgzGx<+YAF)Aq+^7JpZDI%IIP@ne?Gg1pC`L>_oLg*$M#WoYOSB=4G56cU3(y7Ky}JC+UkO4 zfnF@Uc~A!iUPH69X6r$C65_5kER4Bn!#4e6cE;7I>vVJpi}AI1XNbuwQFhJoIg}u9 zrX+xpA2BY4ZRKnuWSz`j<}C~V<7yfh+M7Lqr5AHdEU!BfNh!z-^md4>v>$;#_+YcC zUYV$bVSp9JvOMYH;5xm)bghlbrb7jd8)nH~3j~3c)HXMrSju^qq7Mt+_hlDI0IQaS4*}>*GCS5h#DoG( z{f|kCZb_30dMa^1;5}H1>`Uu%OEbvjA%yBrK~$G`eh(WRG%APsAw%lDm0~0m^}gz4 zB&dNcO`3WHMB8Nv>cAiDk-=`p;GQmCtt*)Y@)0dskTlE1>)=m8{RrP<`dSpPD0Jz` zGYC`GEuT!loflEoG_=rbT<-6r0My!44j8m(>IH9y+8)#uX+NGkNKmzJrrmn|0-aF(02mzQl4jf$}8!!6%L=jNBfs7m*Lg zOtlO3527e?zlWo~2PE9EPU6mqzrc!=b+r4i;@7LyQ3kr0ADOqS>T56zbgD-IuNlS^siX1Nr?*5lqc<*hKA-h((9#|`cHN|}0Rmefy zLJxP~lXR3m9kZ)PZ68su01YKdewpGAbA!0XRX25~@FECXpb@F|Ux{>TB1p7gO8tH51I0hZ6YAnh?z0^Y#q5JQ)QJtGc+h9#sKG2jH`#@4 zAVVt!#~9!gl}+f2^yT%pwSh=}9{_2HjnAQI+ZHstG}f|ZS-DAW4Tslr=dx`wQLN@& z&YsAjKgr{a!JZt9D}*|0(dDyllijIp$$c|u>!@x|p^;}1DsR!xI(0{Yr*aQ#qRmQv z#`=_HPFJP}ohh%Tsxq6};UmG_W9$m!&SDC7z)NI5R0;H*-pd#y<|eo!&MDXsC$^qn zY{{8Zf3%%u?biAwP4F^@HPQ%`Ly@oeB0zJ8os&BOy-rCKNHyCk4Y_esW$zh{P zFQ`_uo}rt`peV5ocK}h}(y6CL#2%&k>7lWY(k1yFF_N)dml>4{8%i1;D09$V=Vw<| zF07ag_?7}Qv!~+cFz`-Vu-At5%-*jVKdG*9TFA~6K8$|9TRkwllNt*qnbc=IE-i_B z`3d9F7)fk1!0_SHjI

2I4!O5!4P!o9FdthA^rF6wTyi-IVrAoCO`4)AplNCFn!N zT@I0;^XDgM+C{p11{T9^59rRx;45~=v9l%ySH7Ov?RIAcP*HecRM&=SMn^YCF$bL5 ze8v8pGG|22Ag~#lSCAZ8&Vcy_vGPJ4b|0|ti-kZnIUjWw9S_x?oAS>sX#S|NL@Q)s zLed-6m2F~;KTTb@(+C(Lhpm5QP#zv)pNW()6q#kGMGwNXLLVflA7NM;st__#N2#nq z7rJtQXAtd0ORx?@6dt-)C)Le+T0#7y$-|QONtPm0738c|;&T9UUzp!&Rrb#MXjZzm ztm>}`YKJ$};#_6IPF$3`9&zfaSEBTXiB}$lS3_m@Dsq0Ub+#4V6W8F5mcU;$u4QHJ zsItr0H2VVEn4FU4;S_pMr7(f)yO8gbTYQ15h8vk9+7-xj9cjoPNzH1_1;}2KZi9Y? zWdh@LN_YwECKDHvM%E0LnR+0%X~;Kga))>GNMBbMGKtU`+z)e=Ar;CGL&{}61OPh( z>d6PhO>*b+vm^X+#WnjDi~PCtY47`0la*jdhd56BH71NZa&Ce7T167k&=T=Rmj9+` zXZO^)O~4Q6)IC_4o6kD>?u6Uc+4h~(zLtNKK^Tkd*1u28d`EXTZkVs^t*mcsmhYK9 z)-Qm)xk7gTOal8lyoWvp4TCZ=wV>9-fmvOonmWehE@jv61X{RU1?!=Konc~|qGwe} zk-sapc>f33so3Fh>{-bdWXO;iJ2CI*;sXz1PZ84@l{Z<>$kRU7JATijyGpnG2gA>1 zFf#g>#Gy+9s8vX)um(wa!)~c*y!c_^9ToN+}xvDMEKUJH@}Iu4;n9s-&dS1x8X)pBLz}RvFZ`6qQe|1|$y(m~^|J1NDKmI?iV`VIy zoc@T+zZ1EDquGDS;?>Fj5}Ko5izz9zTC`AQd)( z8#5J{zbg#C00XjNWiwh0^_ZfDGcnTMpmrb|Or^DXXF$2u;uEqgOJ}RaAi0n?$)^gY zL8%^_$d^Fp_p@`$v8_rgGoYX@c)%;DDJG-M-ylSHlh0AsQDs$8PK5rD+as!TJ7n{? zP24vX9`D@3F_d>fHN zp3vg(!Z*<#7LN^h>1O){8Cgs;2Z}>^=#i{d=U&uYs!#rFAT>#*?`?M4DLjQ0XT+6Kfx$uQL=#>>*pr>&=rOVg9kGYc+Y}kd%ogOcQ|;( zk$(wWsDO%_j6cFw;vYLZ{{vzBA1JsPIGX$~!nRpO=Px997FSSHl2Jj2==iGyrA+dl zB@~1(=}BP|mY4%t&s?dcHnvS&p@vv zWVO4!UcIH(0;SyHhae1w5LJTP**nRhyZGW;AISd_6%rq;(h%J#Uv1Z+P;Xf~UO-`d zY1-3>>GmBf?p1ndBQp%6uL3VR?_+{V=lg(>3f^^Wj!T9c!4_KDY}`6j)U#H@f$B6} zPkD;#&w_`7(=@LbLKx>lVSor}jj1>gEbql3se5Y`~ZDBx8U%xDWR6?Tf~2trVS4 zOwpMxc4eXbT@WLcW{h69wfK`hRYk=LEwE##z7_k8*}fC)V)wLkkFsW%ezFezQqzUl zA^SQ}jS`EFdt9n(-9?WBsz8V!@sFr+!G!+HOkHJEJki!%x6P(S>$&&WzUiQY!|gkL*XR z?4FrzH;(4Kg`|?Trlm7k2T)|iF8Tvm3ypElh>qHvb?hTjPyy$9c=eO;;*yJcJO<_l z{YLt^w2{ z?<}uFWtsIWh-jE)i0TKEZP&mM10&ajB5sBE9n1cx`p(^SvfEWwhM_(MOr5jXjy0Fv z$}HAEHtI3$^*pDPq{%5tn;h3#0+q0OYw&*dD)2s*Vgsz{oUKp+kI$s z37FYWyuu#z6Kghn8mQsWMkX^?)0ru^9u7S}pHE=?XfsS;0LmooSpj7Pmtt)gkHz_o zjY74g7F`M~kCh7Lz}5^_i8qb4*ak9|%4(f-i&`*^_T&lz;lp(VPkw5!#sXW5tI7QG zoGkin70W95Md>HtH_0ILmWd#J{1!2wVWkCzC~O9TS)1sgY;naZnZ}|QQOiW)bXH45 zVdqCn{_h@!!)tUn1DdZ~SYrsy`Avrf!~KP`a}UPqS28C9VTLjT|2Rt;)<6PnGJw=V zHCEe4k1(Sjl#@UQ1}qT(O%1sC=SGMIXE14$!IpfhW8&@kud#O~Gtzd95u2@fdQtTE zM1mtmWHA?%ew{5NPi2_Ix{R^hlce!nv6?9O5j<&QZqgZqCf&G@H^3t**94TyRw{v4w=8u62Tqyc51t~By~xVMp1x~UZ7=wVBjmpHPi*3r4dEp{x{ zuBc|T;UdAKpA#(0%PE`I5)cAz1@9}wY^``DxIRQSH#%6QDcX^g5D&bNOgx1ORDPj; zlJwrnQd{qvRzGZ)rX@!qMZ;4FhUVDWC;%LNyPu;b8`}5R81fJPEML#knN_B@Qc4?e zirNG)TF&8kg(xQ@iLxzL!`QxcDT!>PJ=}M|@_}q&&+PUQ%uO7wb0_hrd2K(Q3)LB$XCTBTj zRXVZWp;b;K*kl=V;-Y%gOpS|U@v62MzP*<@chIXDUnU)(3JN|+XIHW-kw(Xsm}RjG zI_Y6u!9hfS>I*`I4nvBW>la6pFhg4DKG>(ot@|o2MnQHf#FtqYRQ>kOf!Qvvfv^*c z{B(+)qlf~m;QUg18Yuw;eg16e@S_q9M$Ffk5#{8)j;*ml=c<(HXwCB_GkFqFS+xsG z&%H1^GEJ{X4&8Y~doraD=T}EOa$snL>|Nb?oA#@@eg%iTiXExLF|DX*v)p4acyiUl zuiB*-pM|LX+@&2j$D`g#3BkOr4DXB0x=pE5j{{A0?J;^03Bv0}FKf zS=t%YjM=GPTOS&YdGqSMs1RTm;+@SJpZpZ zH$vqlFC={f0xJH;(`EmIG3wub@OPW{KU_fR?*(;Ank`jU+3ZZKCx8;jR+43-O2X)6GPM67wx=q zdx6u0WP~E*?}JK6HU@N4Ri&&YW<9aOT(!Vct~gf$cP$}n>rdT(wL=JENYl0wjXtRs z^xb7(E&$#`){s)Dx=Nb6S_rPf=Ewp`^Xr~R7o%$tUq8}jM?glM3d8_sigwy`iZ#fk zy#;U_OR}&lS~hA0_Kp8m6GQg!X!s zdNh)hZ_2J5zoDY%*P_B%wyhVYbklC5J5 zdG{Wk^|moipJ4cQXzHLn1B-+NbEU~A=?Z8Msa*X{fv_O3)-Jp=4_=oe_lCG6Y4FHg zf#vr)`RK;lzG=-+|hj6hGTuYR^6C(KD;0;k-zWqR~ zkeayC zbNX1{5b>!78w@X(AR30{`zOMp{u8|N*I-G`beAZ2c|75^9b7)zYSp$bjv>RmkJZuM zr@;z`MGjtD*NfHVtk%*-m{;9EH9dmxO0B&rJTc&hFvoQgB01XKz>Om#jn@|qo2N#Y zbx_o~pI_;>fqia;zAPh-`>=p-V6_RBw?6kgmBZQ}>o$snTQhj-0PKXIa5QGuuNwUvT4FkzJBn3S?X=xW#AbE1aN+k_6A96sFq@4N>CUr7uA(kcA!#enDA0S$4*>&RnsSJ>Z z^YL&!ULw!&SalLwU3`vdZam}&5o5_mxvJAe?B6G}D z%V?4BnoT)5#Skwjn~=)TdOq|`<&j-M;*eum+B zxp;atDi-ps{6fnwn`zJ zP(pf~PR#Kl)@Be0=h~M4PZ)&9(Uz{VBX~)7ld^_OWMMjts&sb-HH*vw!%9mN75T!! zgshG}2R(U5OK7h;i7kLvtsQklY(}TF>sQox&eAVw!ybXgkod0>{SnLyl$q#LlP9i7##9uASfU*=i z7(-+o4h>%}_(D4q`IYG_%#x|piK@Qeot)6DJ3-*vzJKHbXE6Nh1U%coAOS*-lhIRU zD{8)h82M-72@oYIc*Agif+(F<8LHu9MEL3@Z`aRCr21QZEwR$P@)F3tSyi|6ZR~gr zoBXNxC5JYSqs)2v&D%OVnVZ?y`8i~7wqjTKERP{6FmSxOoQ_)Oe1-13H9js~6 z+qW?2;RSL`vL@JVNR-G!|HxIk6+l0*Q|)gY{p!|QaZ}%`h{wD}cMgMn{V&jSuM-yt zmDUazsvw+Z;}881E~!w1$&buJ$Pjs{u8C=#K=Sn)p8xX0H`Z$@I`IBQ0*8$)st3rcn;&~ke8Gikv4PBAh$S~MfL)J3`1Ue0;G$q zlY@jSKWeI;WCE3zBWVwO#Nq1R=5#U4qf z+@N~a(?zqr8S?2Ea(9D<>d9+FQauji(y<(U4gbP+e*O5Jr^fXW7pmI(peCxz^fB6{ z9OA5^J>zYU#ma-r5nM2aIfw>UOvENdT8kKv5EvQz0GA3Ju~~4OdEHooo#5>KG`;C7 znRX;%GyV78-uipdhVkcD`9d!!{=(tRoe`fPBJrzR^SH9HU{=Bt}|1at8|u>`#CxG zfSb@A{j|pQ0nds8f*WXpD#fL7?}iLXWzFkJQ-hdks@z!)2F3Nc8zm&19)r?LE2tcX zNS{+*a8~kPk2?zeDa`3F0Y|B-Hc9Inq5ROuN!F&}oM7kiRx9xFc=2;W=w1cWoSj)k zls1Hu%&Q$>1jr8_>`Tg{^p>c;Zy?W$2*aWVL63V9f(nTmF9O=yzu-%F$J z=)YjJm`AP}nNqI>CrPmgrLR{$)IqlrVY7%)ekniz0+_b@8JH#yZvP3Ukxr9ef3=My&b;%~B0+tA>t`Cgp)T|KL{$a;Ulh5-xy2(E_-qObIGQ zwQW~Q2<=RDE#lDc;As&ALPS_J;l(qa>HULQTQv!oFoL~!ENJbHc+lngpEku%2NkK| zkz45cx2l^YkodHkDIc>+wdJPCurX#raImc*_ZDNz7rU~%<-YAYL?oHEb*9>ZJM8+J z8TZJcX<<~KKgdm%1Uq`U4T2+1tv_o@^6(hwM=BuIKr^$7EhxZ%B?3|$C&|*BNpg)% zw}~$Ga>x*C6(S0k<-m(g#H#gHk4I=Mv2YYMb+BuksnG)AiZv*ZfnZ%Nj?eS-2T*nubUuy^MXI9DL{54=l;0{?&ccEXrw$Pt}NmXS*dZKwx$Ow48}K z`M|`j`-wV~UWN;w!EsLp)N-gzV}oT+aXiYsSU(bMN=ye3BzsTsJFVAp==QU(Ooic8 zwf59CW6&&OpP-OJoIa+bTu~JoXfZHH1Ba&sc6N4d+Wh#uA6)ZmGyl>I0(1Tm_59k1 zn%!+W*o=+Nr!!hoQ|;~Al&j`8qF=e^v>g?B9nYMh42g)nWsIAaidA`L_St3du&l#; z=wZ_|Huu;M@0Rs1HVfyh2a5_wM|XFx>d>qwRH;mdGwP)-z+r63z#Y#Jo((={)FFDm zNzIlj?}M;3AGhISXsk(j^9-^di;($4dvsCAjXyRiEmgOsPZR5DvEvKUlAK0ydM(>f z-4GB1+*a|IJmIy)L#FD&xr}lfue?<>e_u&9a0w)E1D-saCU*rI8lPjC*6|v&Fq|B_ z5d$kcF0F866U(HQshgG>qE~W4CKc)to)<$F51tunAN^S3N+%@qQ0Ar_r#veayj@@4 zx7~x=H05qA#qq-A2{Kx&WSiM=H|?8j+oR1)y1PxkB=yNA!}F&OoNzP*t! z?M5kfR5$XnXb?^oWR@64{Y((x^95|Ri?I7YV!c;fi|<<>Ckw5ejF1%QcP4Had0@n-EOH%lr1G9tB(t6?OU{ zv-+CUf~E4B2N@IA(I_#iC2_~)S8W{IgDNBr^DaZAsR?;w&*^p)pH59rnm%~3z^JaU zmu)=Tai<8Zd%P=NEo%oc!~0%gpEri`W$2@@XKX@tesZkt(zz6skIaGw#`_Y- z;-9O~C1ow#oiP50(jn%E-5!bHkn_aMg8^(59VORXg)ncEV6RN4R+3pTV=LcfKo9$r z{Q0DJ$srxtsI(FHdGpwxGk*9eN|#TqWTC!(gd0;gGV%Kv$w9jIaPzu|5$P_n1=+5z zC1pw`1Kn0fA%?MM_m~*XFb?YDUd)4KG>&fR9656-InB|a zLfgqf^4MLQ^{s?mxGd8q>m5k(2EO;AzN|#KFN(lcFl%-N%#`fpM0WU9$4Pt;9&;SV zxJ#gd+CHGHbn>3olG5t7!2aLjiSQC18UV}cY)bhYj7;`PIj$tB2gk?~EFT*KzWJx9 z0(>h*Nu5Z8G6^~&L97L;ba`iIa>)Efmf1@@YfUUM19I-f5$IDB{#oh@EHuGBhLITr zHFDch4~JXEk)-mVwhjV*RvFQ)1Z}jJ(gd4GV|w486mf%4g|9qO!5eFP_@EH zjNP&ZChFv&%!HKX{FKF4+sN$etYy2*R}4{JE?cHFn6n1uN6e$drldO7GnC z2!Xm;W%onnuKU73GyIZ9DS&8I*|8ds+-g{IUW3?eXoC`XCdb+bkJ?WgAxPagD$w&&`2D;xrxQZbmp zrZuedVy5dI7Ctkr+*(4kGt64=kh)Jdl*DBX^gFPwl`g^H;TU&X)0RCLeIo&-C~?@f z?#_hl(nfaxnaNSrNWa!X=e{>wV=;^wi!c_ek1We?3 zAwJe=bZkg<)F@%hpL;&6c9+Zyuwoe83HsSgTCsv&OzY_e!)eQ74B1C=x&SRxBOlKF zuJlR5%#mhitV~Kxb@E$X=uwoshEO_;Sa_q=vlrXR8$Z&h9eR{(L9V2^n}ydyDFmj_ z)XyIiI>pEt#7o-K z2~@r5^ma0L*Mqt^vlQNzn9dm@ zS6h-KQtUGj#a`|h^-FI`>4hudL*37G4ne1XhDFHqQM#xxc$ps84?3hd2tkRxZ28wC8_oR61W;L^WXJEH=)bk9P z9nXQ4cOUQSD3`)_EQaM{MYnTrg0fZ%az`LfKIiDYnWaQgqq=t264werH~U>X2)eJc zg*In&&M_oqE;m<*Oh{7>)hP@=+@C#)rsEbH`w5;Ee^3jrFE#Plsn+t+Gga&~3*7m!vLk2giH9RQPdyoi1$mQ0 z4GXLQ{YxrjH#VILC_T?9uX0Sy9w}gMJ)o#`eFH;&Mj+`-f!$l>ymk0-%iGu0q8G(^pYO8= zYyMR6Z0_jml$GrtzZ=I1FO2CJCZmulfYi1vtOAeT3($_8P#YXo7wf;>sDf=Xt-`qQ z)e8%hIvlRRZ954`L~VLsIvdah;VMfQ{+=o=#QGM?wY+6}atYV(B zCWlw#Jbd?NPEZu5^)+B_v;WtMWei=a6Wi>9~x}SD_IeKHSUcfDNKlU+Go^%K0Jr?@6)5|T{ z2Ov2r16VAFv$nhT29_aSDBu1&nl55Odo3N;_$7rH((F^3NLZiX{0o9SOQ_EVX2*nf zCC+4-x87B9;uvfx6>*d-ahvq&V2>EeaX)13GFY~9GStU9ta8`AE2BH;x&!W*B|#*A zypJOo%5x@%3GdKD7h?4Fl*9X^VOGiEbZPA=7nWBNERt$7uPM%CzhpHu2h@5f6O6EJ zaU^#<_wSdnjoplv)@)0+4uxl*HByMdNGvX~F$YzVsSX>J4uVe|?r2^^hjm;ju#Uib zrdJfLCzw%$Hxg!^$+)@n%?UKY{HXm>Xkx71pKb|1&=OdU7F4a}*bxkaFy-8iv@3^9 zN3N#z_4v@Fyv_h&W;nh6t7Kfa*F1N0CFXO4nyRZlDjOqwmyw&TW2 zHi)=$WgH-uF?KTc6|VJX6i{wVl%v>k`9|PdmZi_R;o`j}Bg^Zth^1{0ck zt@_0a>vl&IhPqpZ1QN5E@c}+xdNwQ%W%dn}3h?ijVl_L0BdA4W`!SQtRH_Jw~YTbl|Y(ce>5&*J6DrfQyj*?&RS)WP0JT zUV)Fh_U3gS>bSq(2nh&W5VTEm7W&eOpm$?pyo+n3zTf2v;_m$w^YHo|c|Zh^s&qjM zgtVEa^K? zp1FO*Fvf7|aHxZUUcoWqs>ML7{nu6)@E18oeAWjhD(5SO=C+YT^QSXF z+1|DQ`kTv~o7{#T`B6dkaaAF4ds{bxD_$M}-vze~!S-20mMP#P7wmI;y|eD6SwS~* zohxCaIrk+Uwz0x*Vx`b5gD7ifyEp9O8*{ZR_eA7Ce{Ue7JG9{2`TL;C{1HkIt z67oId*2C2ivIi5!K+fF2RE{lh2LpG#m@f@h%mfXEw0Sl*F*nc%ezQk0B7t^EOvivX zLum_OE$Y1;F)~m@0SF*6V9Y22_*YQr7|#SKIr%b^v`}-O%R#RfbP=k0F(^-%Uj6Y+ zje7%oTzl>acv7uHNew25S#OkhZScGeZWCAzw2+PpR^qLs!GYN!pWXuBL z1<;R-@Gl;uSU*ZY4?Q(ivZ5}{qcU!>_Khf^diddW!UvY-4%)5-fsj_|x+ZyFl<=WJ ztQ_@Vokv)%7j7`*_!A->KN2^w=#GhtuEF#*ms0E~Rs_q_8@0E2=z+~+JU3-pVjc}S(cdFUo zwyWA&MJw!~xx1HUenLwaTAh+nDkZZ{uQ3~eH6uF)-u9TZpm5aWDAK4ti7Hsx`B-ol zyy}J9^blHDKSvOyssBCIlzc5uaKC7uDnyfUnDTsX8VId$DWIl|T)mM8VWjw znw0u1BNj*P1d*>}HZE5E1ee+ij+WCU} zeQRjN2ZNTTMER*I*eb&0a-9x{W|ltj>;^J=O_DmZ+wOuAue0Pq40y{-sIAWUV0_aZ zfb7TzW;=nqxi9Oa=$=MR3->E!bXL_yP({4r?)OT8r&@0E&E00>$|4Mypt7j$3C5b$ zqQiYEhhzv!+@#E9yCe_y;ZmDtfiH_s&8-{x<2eOXsJ1Z7B>Zy%2h-#{L&C-!&mV2wG!Oh`+*clAg`ohMc`%(b$XthG zg6Nub1=JE-p)e=CYPAeK$j3*pqipI(DzKO7Coto4DO$JjB%>zM`KZ$!TNCnlGmOkm z(pc!eQ)X~`SPi2RGPl5v88|NMA!M>(L9I70vvOrXNi~PF0K6XF$?rLu)a1mSyw%jEa?YM#p6ii_@_2a9aCS{e*%E$9R5~ge@pNjNeT{NPlR#bbIm%t|sCQngW*e zN&LRljfQpLyEncwwn;lO9jK-%wsmC<(}N5QcQQa#1OW%vh@=_3a_wnbQ60AQGy>Hr zJj#maOJwfr*!qbw9(^JQ)+4^c)g#f)pAn1E@Y{DdV%kEQVoOYh1J;K~TM;q8=gwoy zONWRjeM-+Jn4Hl1I*1waI%o4^ZI%VOX+8Ut&oG|ZV|X^)(|RK3GI)X{{iQ^<@*!~+ zd%npL>u3Lqw`O}jVGNy)ep%7c?!`c6!K&gDn_R}ce z=!9J|Ay}rfehkH%o*b~CPSs;4>3Bb_`-_97@qs@`w7<|y?n+s@;uXuc!dc;nLU}`7 zu0<-bZy2!;GGRFLLwS5=7}>&R-%%-hr383N%hPPUL>NrwHyq`VUGgu|LajNP4e;sT z9dR-lBc~g<=tnFhUMY&_EadN=RC!x}et!LI_QF{~2sFWQp6h7E6!KWZ3vnOZv4xi|e)nTHIgghWErYWd?BEWXI+kl6|A4yDOtffTr8;Ia1zqd1&+Q z0Vo_m%SzSqRhK69ls8&dw%3rQ{q9Rpg1;v8oCIKQFrMf(9;kl?O;l-$T=iGOo%P{b znR8(n&AUX+rb+XTr2-X&|Jig)V5P z5Dc#ki1ubP0mt$M^bII27$ZE!dElfz%qz%~d^3u(>kX_wRx{$z)MdYVD@*PCvDgrE|PF?K!7hL}W!) z+YCCXWu;2uSL05Ay7l%=ORG1Ps{kto4Rj5_BL_e7U>`%wh%%J?L%O@fLhx-CG+v|T zmLm>%0td`u7(gHw_RLHm*NPyD_Ly4ex#6RBY$#k}1p*srPeA3jL;53L=MEUKOlGJi zC@__N^s~)GbWIr$Ctq#El?nx#f;4LrRLE}{YKi_pf_ocL@zHk15Ha>1{%Gkz7Ra^s z5Tp`|(L?2S9lhQC3$|+h+sug)F6QRu0HkolUfl$X6=WIfUsgA_;*@WPj&_Thqk}#j zh-+7<9~NboOi*GLQG%`m#ydII7x_||B&dZkseewAwtJmg1Qd+Rx>7{de zXrw@V!fh+K&_E;L!TA15mRz$zu#DIp8f^=nR%5qyqElC9s%rRFmF5wDN)CJ zlKOY|3R_5_BF2sA7c#qm5?j{sNH(EweGmJ1De2?KS4upJW$N{;1$-?9Z`I-zJuLlo z>$Sw&PAH);W_~soQeVp;V@*de)fm3|4VB4eCt6I^MRw4Qc35GCTA&}Xb(8TRO8AxpBiF@zg0{}TdXU__o|H^1A#mx2+G%b+ zPmJmol&{?&7LgGZopU|#!=$zx8p0G1w2tc)$xW(Ytu)c%ZE+l{beWrskGeuNvX-f& z(-Sg#R;3z4$x$E{VH^}VL^@h9?6qy(TkYY@IFsj#|3nDl z%@@KDU?EI>ZM05FSmo5)_WQ29BPR2m(vaKJ~VM{%QF%EU_|=AJg8$`XM7NYWXD^@~YwI#Lb%Z z=Ax8v+;ogtPa2@-%I!Lp81!`1M#=~G38d`s@sMlPr`p9QtgkNPT2ZL)1@h`M+t_5R%N4O{uu^2dW#ck>R&5C^($5X^U*me;(|Yw;4l!35Fl zc0`8l^!O6O6Bw8!GPeun+*>k{#e0mjSKV^Pv0DVZY~ZRpIjo|lULo0A_kG= zqztu;8NjTfahqcAhUEDw?q2Q{lecb*9Sno`Mob18)Nr~ zUrj*NM@d5{;muUV5z>NhE82OM$3WJHj}perI84$D_32u&+UwH}Ul|)7g6xnHN5JajAdb?LB4>Bq1c^9d7vY zvdYZT+Xd^hYFm#lJi3lIW-m{;4@ejq5D>-<50n5fq{q)w?eWj=uEHZPWi~nPPp8>+ zvy0=F(VNkU;&A)cd$Vspn}H?YQX_IABX_LN_ZojZ?E?r3zj{wfO9M^=(+y5}GtS-( zohbl+vf+IP{$gQ~cVVzcYO(XFy7oH@eIto0yWpG4!7=gXu89$}4I|6Tf$efJ%;7hC zu4>n}9ga_MpLzjieP9>givZu%xVq0jm23wuA4j7S#ketEP0hYXTEN~zLcc`!hzEjw z;sYd154b{7we|7O?@XL~FMMk5<>ZPvX8I|uQ0U$ELj zzBHy(d>NZRkk*aH&RO!+nIH9DE_x>0tvMirf(Mqqol+I&A>wqh06s5Osn9+wctU<& zB$fsMA1l3w8pq4t#=$fZ1=w1I@h(K#kU!UFASC)?6>#((t-3Jtne)yz2G|TRcz~us z#|_degBs3b4ENNl(6nFvdJ(0~qD`<*WzX-ovl+$pLts;d zz9w_NU96frWKTaY+PclhRfn@V4&D+jU1l&-05YEryqMnlIG;_lW1vEE;$D>-e`MR? zTzu^S-6s4f+qs5{XNNd&^}fzN_-QvT5RpItKBkkNMHg>JzsKrtygPl&PbUrp3<&@L z4h}$VNg^@gH_|Ev003b1@n!%(2Cy--v$wXgH?(sy)ip)I$Y*pp5M9~%iQ5i@en&`) zTt&Pcb*x-LuZHaB7bbS!3giRxti(Vlc-u;th`||N8lOTD3YbvR2txK6ls$rVL=cNY zw;&>NeNp^jPH|oXm+JXRG}!@V2_k&;+((b%{^J_pmWgI^9Kk5&Vq_TyMGghDxk%Fp zR}vOQk&45z0tcO-Qqmjib2g72D53Gn^%d>+lD!2BuvAK2`OCpimkTKeMC_5171s*Y zl$E<>r1wtKGd$!>cCz#dI_zI!#_pJO3X@pGF0WORT<=dU+SXbnIL^fy*vtI6+6FkI z^EQz)tw+`j$!H)Y*0M2kY4^ghtFhL?mp)}mEhUMSQWVqqPFkRR{-W*};UPvvL%n2U4MhjQ2vWMlx1)MkuewdzMNEacHkEH6EYvG%3F60T8 zv783>K&!vU8s{*l24zr>cBcEbQ6!&}c+`|=`H@0pQF91Tp#*%N7%Xsp>RaW0^%cvG zvs|Y_84J@W{UJW;inn8||OM zQMiT>mD1-TDP9 zr%pV-{F4Yw)NK^K>M?oyoJ?+aE4+AhW>Xb5CMTRl>QYWa&rg^#vP6CxXGXr7IxUs3 znHc&97WSrkK+itXhAk+FC5PQ3)K?o?D6Ch$m?2RMV%l1Z!Y2PwJ*%*o?bAr-O+9bL z*BsNjmd(MFf^+2hDQ#P<*)47b@U(oZA3FMUHIuj3Y8mUA)lZKhbVkD-E!i=$vH@ zqp8Y|eIZXj3P{+oQ$fB2<$a*2(8N4H;DuR zgN(Bi)M{RsgYa{m@b#Ti9LB~^_8pvAX<=7jqZLubl|G%~F#ur)p7MtE%*V!dtw8x% zV-u;7_UxkB_4Cu3Tv7eW;`tL%T^Z8>o9TcY21MIEXLb>eVEF_;GV1;SZj{PgSks#zM~zV$T4x~l4vMD(`{T{>&n}^ zrzq~01?k%seaBebcs0-p5FSv3rM879Tv9oG9o_T84KAe&pOYWAPx$P7#Hapk1gdE# zk-B#L+NS9Ztze@(Qc;b9gJ;B_R-V4Rox5wbS2c+asjn&BtJo^Wv&&y736Y#{9T}eC z`y}bjDUj__{=Dy6b9=H3_rRM5VsoLgO`I2RVmrJ}ICDX4-pLAlU>V{qw$dmSMJm;G|X4dY#QIfSijgrB7(eU2o5=bNSeZZ+Em z%LnKEb-s(JAw)g=(D7!S6FQcZa*XX}$v0ZBiiJ>3fV`R&^4c8b*qO z9_)0c^QT^is5o812=N{gzv*k5T{|3UzN{ixQPx|&R{;@nT`yIMnjz~bgg0DFWPlu;h zmp2=~-=y#P+omHw0RWVK+JqsI8>2Hy4VXr-nvOHSYw^L(5KdFC!^X!Q@SQl=(ix!Lr2kW_OqUVfLm!Q zP8ksd!^08Z4i0aTcJ>jnhKY+9{+w~nWK|vqZ3N&=4QhQ#BmN}jHf9(*Po6JytVnGz zu152zrSZ`ZD!dAEhxhC&7u${1|J!T^|c-1IhN4GN#xTBBsy%{0dO2;MPn zxH=qrG#C6Exme9;5u)VOO9-agig`(T6FT#85AT--PCd9!%x#d(RX2B)uhLeC#6@1n z!$bgWw3Ykg;{t=?OjLbGs19L5^Mrur8-F+y0w&hBFh<#xfp%0};}?ROQSDl^9fYde zM|eTbkeDJ8Fj)c3HkO{@<@q-Uokmr$p7nQE`xg3ID58MO9!=7T*D*CNk=>JymVA%z zwiF>!3>#WAt7WiAJD8bqKK1{}!|WZrOa*YcOdTx@)g9#e?v5@Lrgq?T?SMy9Vc-Be zweFv2g^{SMx?N}&D~qq z5JR-_RBOUn6OE_3F8ryNc-$t;SqURF~5;i3wF000{Q;?wqa`X4E>sTGZxu9Gf} zzO^Ndp01&Vfuof%%^&IX@5!~y|4y}eZEP$|^>rOgt*xkk0e)Q?zx4c)UpjwDU^E?U zFD@a|urMGvU6&%HAAt=Fo3mG^| zJ0Z}Cctth03E$OaMF6Lpepz_qaN`q+A=YC|TN=zf(i20M8?S1}d?Mx7NS&~PVaIvG zd5X=1Ko)SUPRLF9c=KFPXx@ybAuEk-Sef!k_-m2SPzws7`pWX1c=r}a10<&4R|;)jx;|*Q-+S-Z5&X|J4#4uCd(Y0$-rCVl-_V}sqw4ram;7FB{6hVb zKX5>Qst~Lwp9p(C{%-Wqfx7=zm4l(Z!~gFSrSJi<|Hs+-rR z3~=;{#Ie?!f^8-b=LH$|USPFv$Htv!8IIt^Y~lsSFH|Ms3I5c5LT;N*+=~FJ%S%FY zz)J8!<|)XBeKmZ*80P!Df0RYZ?DgUMIuL|z3bpKCL_tEZ`ZmbUj zf7kuDqV=x;nEx=~7eMmg1OBLb{|fZaBl!gi`WMizknmUSQGYX}@!z)pTS)mU2-a_q zlm86)TU`1p(wE;zL{NY3*nf?1e?=qvji&$qK>J6${3|ToZ`jiR2Kz@C{wqAsZ}`Xm z3;uU+{3~U6zbR9K{*N90&!G5MxZm9=@!#V97Wn=O^1C~Y{%6R4=}y2f|L*91kHddu z*YEDc_y0iqKe$uqe}nz+-D&avg8xtM^!LCzIg0CTON>_h<6&UX(5Gl&#?Zf nrNCF9{og71{W||Y9_~N$Eg5kT&|gY1K3?e`=}Ah*udV+Nfq1kU literal 0 HcmV?d00001 diff --git a/spring-cloud-security/personservice/pom.xml b/spring-cloud-security/personservice/pom.xml new file mode 100644 index 0000000000..74bd67d031 --- /dev/null +++ b/spring-cloud-security/personservice/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.baeldung.service + personservice + 0.0.1-SNAPSHOT + jar + + personservice + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.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 + + + com.google.code.gson + gson + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java new file mode 100644 index 0000000000..58e36faaa7 --- /dev/null +++ b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java @@ -0,0 +1,51 @@ +package com.baeldung.service.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-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java new file mode 100644 index 0000000000..0fc14db64f --- /dev/null +++ b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.service.personservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PersonserviceApplication { + + public static void main(String[] args) { + SpringApplication.run(PersonserviceApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java new file mode 100644 index 0000000000..807aa95736 --- /dev/null +++ b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java @@ -0,0 +1,25 @@ +package com.baeldung.service.personservice.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-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java new file mode 100644 index 0000000000..59351ed621 --- /dev/null +++ b/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java @@ -0,0 +1,31 @@ +package com.baeldung.service.personservice.controller; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.service.model.Person; +import com.google.gson.Gson; + +@RestController +public class PersonInfoController { + + @RequestMapping(value = "/currenttime") + @PreAuthorize("hasAnyRole('ADMIN', 'USER')") + public String currentTime(){ + return LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME); + } + + + @RequestMapping(value = "/person") + @PreAuthorize("hasAnyRole('ADMIN', 'USER')") + public @ResponseBody String personInfo(){ + Gson gson = new Gson(); + String person = gson.toJson(new Person("abir","Dhaka", "Bangladesh",29,"Male")); + return person; + } +} \ No newline at end of file diff --git a/spring-cloud-security/personservice/src/main/resources/application.properties b/spring-cloud-security/personservice/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-cloud-security/personservice/src/main/resources/application.yml b/spring-cloud-security/personservice/src/main/resources/application.yml new file mode 100644 index 0000000000..38dc22c2cd --- /dev/null +++ b/spring-cloud-security/personservice/src/main/resources/application.yml @@ -0,0 +1,25 @@ +# Make the application available at http://localhost:9000 +#spring: +# session: +# store-type: redis + +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-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java b/spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java new file mode 100644 index 0000000000..6e246bc363 --- /dev/null +++ b/spring-cloud-security/personservice/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-security/pubkey.txt b/spring-cloud-security/pubkey.txt new file mode 100644 index 0000000000..2c391ba2dd --- /dev/null +++ b/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-security/springoath2client/pom.xml b/spring-cloud-security/springoath2client/pom.xml new file mode 100644 index 0000000000..833a377410 --- /dev/null +++ b/spring-cloud-security/springoath2client/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + com.example + springoath2 + 0.0.1-SNAPSHOT + jar + + springoath2 + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.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-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java new file mode 100644 index 0000000000..9cfea2faea --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java @@ -0,0 +1,24 @@ +package com.cloud.springwebsite; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Bean; + +import com.cloudsite.filters.pre.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-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java new file mode 100644 index 0000000000..af002080be --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java @@ -0,0 +1,49 @@ +package com.cloud.springwebsite.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-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java new file mode 100644 index 0000000000..829648b43f --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java @@ -0,0 +1,39 @@ +package com.cloud.springwebsite.controller; + +import java.net.URI; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +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; + + + @Value("${person.url}") + private String personUrl; + + + @RequestMapping("/") + @ResponseBody + public String helloFromBaeldung() { + return "Hello From Baeldung!"; + } + + + @RequestMapping("/person") + public ModelAndView person(){ + ModelAndView mav = new ModelAndView("personinfo"); + mav.addObject("person",restOperations.getForObject(personUrl, String.class)); + return mav; + } + +} \ No newline at end of file diff --git a/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java b/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java new file mode 100644 index 0000000000..e9412b5ab6 --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java @@ -0,0 +1,39 @@ +package com.cloudsite.filters.pre; + +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-security/springoath2client/src/main/resources/application.properties b/spring-cloud-security/springoath2client/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-cloud-security/springoath2client/src/main/resources/application.yml b/spring-cloud-security/springoath2client/src/main/resources/application.yml new file mode 100644 index 0000000000..06a950d270 --- /dev/null +++ b/spring-cloud-security/springoath2client/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-security/springoath2client/src/main/resources/static/index.html b/spring-cloud-security/springoath2client/src/main/resources/static/index.html new file mode 100644 index 0000000000..f304980bb1 --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/resources/static/index.html @@ -0,0 +1,60 @@ + + + + + +Demo + + + + + + + + + +

Demo

+
+ With Facebook: click here +
+ + + + + + + \ No newline at end of file diff --git a/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html b/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html new file mode 100644 index 0000000000..9ed0c3cb4f --- /dev/null +++ b/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html @@ -0,0 +1,32 @@ + + + + +My Website - Current time + + + +

Providing Person Information

+

+ Person's information: +

+

+ The current time is: +

+ + + + \ No newline at end of file diff --git a/spring-cloud-security/springoath2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java b/spring-cloud-security/springoath2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java new file mode 100644 index 0000000000..5fa51a61c3 --- /dev/null +++ b/spring-cloud-security/springoath2client/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() { + } + +} From a9d5b406a5da7e5478dacc1f560e5a5cc6c9af1c Mon Sep 17 00:00:00 2001 From: ShyamVeda Date: Wed, 17 Jan 2018 02:21:01 +0530 Subject: [PATCH 020/118] Added port number to response (#3431) --- .../cloud/eureka/client/EurekaClientApplication.java | 7 ++++++- .../eureka-client/src/main/resources/application.yml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) 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: From bc8caf3266848c6c6530cfbf2f754d1c0bc395a6 Mon Sep 17 00:00:00 2001 From: abirkhan04 Date: Wed, 17 Jan 2018 16:42:26 +0600 Subject: [PATCH 021/118] 1. Unnecessary file and code removed. 2. Project is relocated to spring-cloud folder --- spring-cloud-security/personservice.zip | Bin 50761 -> 0 bytes .../src/main/resources/static/index.html | 60 ------------------ .../spring-cloud-security}/README.md | 0 .../spring-cloud-security}/alias.rtf | 0 .../spring-cloud-security}/authserver/pom.xml | 0 .../com/cloudsecurity/auth/AuthServer.java | 0 .../auth/config/AuthServerConfigurer.java | 0 .../auth/config/ResourceServerConfigurer.java | 0 .../auth/config/WebMvcConfigurer.java | 0 .../auth/config/WebSecurityConfigurer.java | 4 +- .../auth/controller/ResourceController.java | 0 .../src/main/resources/application.yml | 0 .../main/resources/certificate/mykeystore.jks | Bin .../src/main/resources/templates/login.html | 0 .../spring-cloud-security}/mykeystore.jks | Bin .../personservice/pom.xml | 0 .../com/baeldung/service/model/Person.java | 0 .../PersonserviceApplication.java | 0 .../config/ResourceConfigurer.java | 0 .../controller/PersonInfoController.java | 0 .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 0 .../PersonserviceApplicationTests.java | 0 .../spring-cloud-security}/pubkey.txt | 0 .../springoath2client/pom.xml | 0 .../com/cloud/springwebsite/CloudSite.java | 0 .../config/SiteSecurityConfigurer.java | 0 .../controller/CloudSiteController.java | 0 .../cloudsite/filters/pre/SimpleFilter.java | 0 .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 0 .../main/resources/templates/personinfo.html | 2 +- .../Springoath2ApplicationTests.java | 0 33 files changed, 3 insertions(+), 63 deletions(-) delete mode 100644 spring-cloud-security/personservice.zip delete mode 100644 spring-cloud-security/springoath2client/src/main/resources/static/index.html rename {spring-cloud-security => spring-cloud/spring-cloud-security}/README.md (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/alias.rtf (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/pom.xml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java (96%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/resources/application.yml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/resources/certificate/mykeystore.jks (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/authserver/src/main/resources/templates/login.html (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/mykeystore.jks (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/pom.xml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/java/com/baeldung/service/model/Person.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/resources/application.properties (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/main/resources/application.yml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/pubkey.txt (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/pom.xml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/resources/application.properties (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/resources/application.yml (100%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/main/resources/templates/personinfo.html (92%) rename {spring-cloud-security => spring-cloud/spring-cloud-security}/springoath2client/src/test/java/com/example/springoath2/Springoath2ApplicationTests.java (100%) diff --git a/spring-cloud-security/personservice.zip b/spring-cloud-security/personservice.zip deleted file mode 100644 index 9c6c9e2ef583448b52fd8acb7c57d1304bbdc30d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50761 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%0DD(x6LIVRq4Ib=H;7V(N<*2Hiz!CxZ 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 diff --git a/spring-cloud-security/springoath2client/src/main/resources/static/index.html b/spring-cloud-security/springoath2client/src/main/resources/static/index.html deleted file mode 100644 index f304980bb1..0000000000 --- a/spring-cloud-security/springoath2client/src/main/resources/static/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - -Demo - - - - - - - - - -

Demo

-
- With Facebook: click here -
- - - - - - - \ No newline at end of file diff --git a/spring-cloud-security/README.md b/spring-cloud/spring-cloud-security/README.md similarity index 100% rename from spring-cloud-security/README.md rename to spring-cloud/spring-cloud-security/README.md diff --git a/spring-cloud-security/alias.rtf b/spring-cloud/spring-cloud-security/alias.rtf similarity index 100% rename from spring-cloud-security/alias.rtf rename to spring-cloud/spring-cloud-security/alias.rtf diff --git a/spring-cloud-security/authserver/pom.xml b/spring-cloud/spring-cloud-security/authserver/pom.xml similarity index 100% rename from spring-cloud-security/authserver/pom.xml rename to spring-cloud/spring-cloud-security/authserver/pom.xml diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java similarity index 100% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/AuthServer.java diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java similarity index 100% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/AuthServerConfigurer.java diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java similarity index 100% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/ResourceServerConfigurer.java diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java similarity index 100% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebMvcConfigurer.java diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java similarity index 96% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java index eb81e65196..9014aa4954 100644 --- a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java +++ b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/config/WebSecurityConfigurer.java @@ -19,8 +19,8 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter { - @Autowired - private OAuth2ClientContext oauth2ClientContext; +// @Autowired +// private OAuth2ClientContext oauth2ClientContext; @Override protected void configure(HttpSecurity http) diff --git a/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java b/spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java similarity index 100% rename from spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java rename to spring-cloud/spring-cloud-security/authserver/src/main/java/com/cloudsecurity/auth/controller/ResourceController.java diff --git a/spring-cloud-security/authserver/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/authserver/src/main/resources/application.yml similarity index 100% rename from spring-cloud-security/authserver/src/main/resources/application.yml rename to spring-cloud/spring-cloud-security/authserver/src/main/resources/application.yml diff --git a/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks b/spring-cloud/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks similarity index 100% rename from spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks rename to spring-cloud/spring-cloud-security/authserver/src/main/resources/certificate/mykeystore.jks diff --git a/spring-cloud-security/authserver/src/main/resources/templates/login.html b/spring-cloud/spring-cloud-security/authserver/src/main/resources/templates/login.html similarity index 100% rename from spring-cloud-security/authserver/src/main/resources/templates/login.html rename to spring-cloud/spring-cloud-security/authserver/src/main/resources/templates/login.html diff --git a/spring-cloud-security/mykeystore.jks b/spring-cloud/spring-cloud-security/mykeystore.jks similarity index 100% rename from spring-cloud-security/mykeystore.jks rename to spring-cloud/spring-cloud-security/mykeystore.jks diff --git a/spring-cloud-security/personservice/pom.xml b/spring-cloud/spring-cloud-security/personservice/pom.xml similarity index 100% rename from spring-cloud-security/personservice/pom.xml rename to spring-cloud/spring-cloud-security/personservice/pom.xml diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java b/spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java similarity index 100% rename from spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java rename to spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/model/Person.java diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java b/spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java similarity index 100% rename from spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java rename to spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/PersonserviceApplication.java diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java b/spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java similarity index 100% rename from spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java rename to spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/config/ResourceConfigurer.java diff --git a/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java b/spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java similarity index 100% rename from spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java rename to spring-cloud/spring-cloud-security/personservice/src/main/java/com/baeldung/service/personservice/controller/PersonInfoController.java diff --git a/spring-cloud-security/personservice/src/main/resources/application.properties b/spring-cloud/spring-cloud-security/personservice/src/main/resources/application.properties similarity index 100% rename from spring-cloud-security/personservice/src/main/resources/application.properties rename to spring-cloud/spring-cloud-security/personservice/src/main/resources/application.properties diff --git a/spring-cloud-security/personservice/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/personservice/src/main/resources/application.yml similarity index 100% rename from spring-cloud-security/personservice/src/main/resources/application.yml rename to spring-cloud/spring-cloud-security/personservice/src/main/resources/application.yml diff --git a/spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java b/spring-cloud/spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java similarity index 100% rename from spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java rename to spring-cloud/spring-cloud-security/personservice/src/test/java/com/baeldung/service/personservice/PersonserviceApplicationTests.java diff --git a/spring-cloud-security/pubkey.txt b/spring-cloud/spring-cloud-security/pubkey.txt similarity index 100% rename from spring-cloud-security/pubkey.txt rename to spring-cloud/spring-cloud-security/pubkey.txt diff --git a/spring-cloud-security/springoath2client/pom.xml b/spring-cloud/spring-cloud-security/springoath2client/pom.xml similarity index 100% rename from spring-cloud-security/springoath2client/pom.xml rename to spring-cloud/spring-cloud-security/springoath2client/pom.xml diff --git a/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java b/spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java similarity index 100% rename from spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java rename to spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/CloudSite.java diff --git a/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java b/spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java similarity index 100% rename from spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java rename to spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/config/SiteSecurityConfigurer.java diff --git a/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java b/spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java similarity index 100% rename from spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java rename to spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloud/springwebsite/controller/CloudSiteController.java diff --git a/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java b/spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java similarity index 100% rename from spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java rename to spring-cloud/spring-cloud-security/springoath2client/src/main/java/com/cloudsite/filters/pre/SimpleFilter.java diff --git a/spring-cloud-security/springoath2client/src/main/resources/application.properties b/spring-cloud/spring-cloud-security/springoath2client/src/main/resources/application.properties similarity index 100% rename from spring-cloud-security/springoath2client/src/main/resources/application.properties rename to spring-cloud/spring-cloud-security/springoath2client/src/main/resources/application.properties diff --git a/spring-cloud-security/springoath2client/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/springoath2client/src/main/resources/application.yml similarity index 100% rename from spring-cloud-security/springoath2client/src/main/resources/application.yml rename to spring-cloud/spring-cloud-security/springoath2client/src/main/resources/application.yml diff --git a/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html b/spring-cloud/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html similarity index 92% rename from spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html rename to spring-cloud/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html index 9ed0c3cb4f..4f8eedfb6a 100644 --- a/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html +++ b/spring-cloud/spring-cloud-security/springoath2client/src/main/resources/templates/personinfo.html @@ -2,7 +2,7 @@ -My Website - Current time +My Website - Getting Personal Information - - -

Providing Person Information

-

- Person's information: -

-

- The current time is: -

- - - - \ No newline at end of file From aa0785ca16d22fea0e459ea797f53309afe9220c Mon Sep 17 00:00:00 2001 From: Doha2012 Date: Wed, 17 Jan 2018 22:59:15 +0200 Subject: [PATCH 027/118] upgrade opened to spring boot 2 (#3441) * upgrade opened to spring boot 2 --- spring-security-openid/pom.xml | 37 +++++++++++++++---- .../config/SpringOpenidApplication.java | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/spring-security-openid/pom.xml b/spring-security-openid/pom.xml index 2149334a5c..4c8112a163 100644 --- a/spring-security-openid/pom.xml +++ b/spring-security-openid/pom.xml @@ -11,12 +11,12 @@ spring-security-openid Spring OpenID sample project - - parent-boot-5 - com.baeldung - 0.0.1-SNAPSHOT - ../parent-boot-5 - + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + @@ -37,13 +37,36 @@ org.springframework.security.oauth spring-security-oauth2 + 2.2.1.RELEASE org.springframework.security spring-security-jwt + 1.0.9.RELEASE - + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + diff --git a/spring-security-openid/src/main/java/org/baeldung/config/SpringOpenidApplication.java b/spring-security-openid/src/main/java/org/baeldung/config/SpringOpenidApplication.java index ed57088c56..1acdba0623 100644 --- a/spring-security-openid/src/main/java/org/baeldung/config/SpringOpenidApplication.java +++ b/spring-security-openid/src/main/java/org/baeldung/config/SpringOpenidApplication.java @@ -2,7 +2,7 @@ package org.baeldung.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication public class SpringOpenidApplication extends SpringBootServletInitializer { From a8f6c9f9fb36e3479546948d7aa50e766e4dd0a2 Mon Sep 17 00:00:00 2001 From: BineshNarayanan Date: Thu, 18 Jan 2018 12:22:06 +0800 Subject: [PATCH 028/118] BAEL-1276 | Adding example of ActiveJDBC --- activejdbc/pom.xml | 129 ++++++++++++++++++ .../main/java/com/baeldung/ActiveJDBCApp.java | 61 +++++++++ .../java/com/baeldung/model/Employee.java | 19 +++ .../main/java/com/baeldung/model/Role.java | 18 +++ .../src/main/migration/_create_tables.sql | 25 ++++ .../src/main/resources/database.properties | 10 ++ .../java/com/baeldung/ActiveJDBCAppTest.java | 51 +++++++ 7 files changed, 313 insertions(+) create mode 100644 activejdbc/pom.xml create mode 100644 activejdbc/src/main/java/com/baeldung/ActiveJDBCApp.java create mode 100644 activejdbc/src/main/java/com/baeldung/model/Employee.java create mode 100644 activejdbc/src/main/java/com/baeldung/model/Role.java create mode 100644 activejdbc/src/main/migration/_create_tables.sql create mode 100644 activejdbc/src/main/resources/database.properties create mode 100644 activejdbc/src/test/java/com/baeldung/ActiveJDBCAppTest.java 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(); + } +} From bff1f7ba12c62f79180854a1e39c87fc40b67bf4 Mon Sep 17 00:00:00 2001 From: chrisoberle Date: Thu, 18 Jan 2018 05:13:33 -0500 Subject: [PATCH 029/118] BAEL-1418 - spring security with extra login fields (#3413) * BAEL-1418 - spring security with extra login fields * change delimeter for username/domain concatenation * remove unnecessary class * move source to spring-5-security module * finish moving example code to spring-5-security module * fix formatting in pom * adjust spacing --- spring-5-security/pom.xml | 19 ++++ .../CustomAuthenticationFilter.java | 54 +++++++++ .../CustomUserDetailsService.java | 32 ++++++ .../CustomUserRepository.java | 26 +++++ .../securityextrafields/SecurityConfig.java | 65 +++++++++++ .../SpringExtraLoginFieldsApplication.java | 13 +++ .../baeldung/securityextrafields/User.java | 23 ++++ .../securityextrafields/UserRepository.java | 7 ++ .../securityextrafields/WebController.java | 51 +++++++++ .../application-extrafields.properties | 1 + .../src/main/resources/static/css/main.css | 18 +++ .../resources/templatesextrafields/index.html | 24 ++++ .../resources/templatesextrafields/login.html | 23 ++++ .../templatesextrafields/user/index.html | 13 +++ .../SecurityExtraFieldsTest.java | 103 ++++++++++++++++++ 15 files changed, 472 insertions(+) create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java create mode 100644 spring-5-security/src/main/resources/application-extrafields.properties create mode 100644 spring-5-security/src/main/resources/static/css/main.css create mode 100644 spring-5-security/src/main/resources/templatesextrafields/index.html create mode 100644 spring-5-security/src/main/resources/templatesextrafields/login.html create mode 100644 spring-5-security/src/main/resources/templatesextrafields/user/index.html create mode 100644 spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java 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/securityextrafields/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..b5d628628d --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java @@ -0,0 +1,54 @@ +package com.baeldung.securityextrafields; + +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 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()); + } + + 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/securityextrafields/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java new file mode 100644 index 0000000000..be02834852 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java @@ -0,0 +1,32 @@ +package com.baeldung.securityextrafields; + +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 CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + public CustomUserDetailsService(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/securityextrafields/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java new file mode 100644 index 0000000000..c86769b016 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.securityextrafields; + +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/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java new file mode 100644 index 0000000000..429f6df972 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -0,0 +1,65 @@ +package com.baeldung.securityextrafields; + +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 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() { + 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/securityextrafields/SpringExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..a779acc75e --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.securityextrafields; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringExtraLoginFieldsApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringExtraLoginFieldsApplication.class, args); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java new file mode 100644 index 0000000000..a5b3a434ae --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java @@ -0,0 +1,23 @@ +package com.baeldung.securityextrafields; + +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 final 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/securityextrafields/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java new file mode 100644 index 0000000000..4ca65b13d5 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.securityextrafields; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java new file mode 100644 index 0000000000..4a8abb4a83 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.securityextrafields; + +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..9299ee6158 --- /dev/null +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -0,0 +1,18 @@ +body { + font-family: sans; + font-size: 1em; +} + +p.error { + font-weight: bold; + color: red; +} + +div.logout { + float: right; +} + +.formfield { + margin: 0.5em; + padding: 0.3em; +} \ No newline at end of file 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..52f6224dfb --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -0,0 +1,24 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+ Logged in user: | + domain: 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..cafec89c15 --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -0,0 +1,23 @@ + + + + Login page + + + + +

Login page

+

Example: user / domain / password

+

Invalid user, password, or domain

+
+ : +
+ : +
+ : +
+ +
+

Back to home 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..a4c1535100 --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -0,0 +1,13 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+

This is a secured page!

+

Back to home page

+ + diff --git a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java new file mode 100644 index 0000000000..cf0701708d --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java @@ -0,0 +1,103 @@ +package com.baeldung.securityextrafields; + +import static org.junit.Assert.assertEquals; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +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.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.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.FilterChainProxy; +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.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringRunner.class) +@SpringJUnitWebConfig +@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class) +public class SecurityExtraFieldsTest { + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) + .apply(springSecurity(springSecurityFilterChain)).build(); + } + + @Test + public void givenRootPathAccess_thenRedirectToIndex() throws Exception { + this.mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("/index*")); + } + + @Test + public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { + this.mockMvc.perform(get("/user/index")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/login")); + } + + @Test + 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); + } +} From 1d29f9be8be57954a598ddb007ff8ec3d155ba48 Mon Sep 17 00:00:00 2001 From: Sergey Petunin Date: Thu, 18 Jan 2018 11:50:38 +0100 Subject: [PATCH 030/118] BAEL-1414 Learn to Fully Leverage Java Server Faces (#3446) --- guest/deep-jsf/README.md | 15 ++++++ guest/deep-jsf/pom.xml | 40 ++++++++++++++++ .../stackify/deepjsf/GreetControllerBean.java | 14 ++++++ .../stackify/deepjsf/PhaseListenerBean.java | 47 ++++++++++++++++++ .../java/com/stackify/deepjsf/UserBean.java | 48 +++++++++++++++++++ .../stackify/deepjsf/UserControllerBean.java | 14 ++++++ .../src/main/webapp/WEB-INF/faces-config.xml | 15 ++++++ guest/deep-jsf/src/main/webapp/greet.xhtml | 11 +++++ guest/deep-jsf/src/main/webapp/hello.xhtml | 10 ++++ guest/deep-jsf/src/main/webapp/index.xhtml | 18 +++++++ guest/deep-jsf/src/main/webapp/register.xhtml | 31 ++++++++++++ 11 files changed, 263 insertions(+) create mode 100644 guest/deep-jsf/README.md create mode 100644 guest/deep-jsf/pom.xml create mode 100644 guest/deep-jsf/src/main/java/com/stackify/deepjsf/GreetControllerBean.java create mode 100644 guest/deep-jsf/src/main/java/com/stackify/deepjsf/PhaseListenerBean.java create mode 100644 guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserBean.java create mode 100644 guest/deep-jsf/src/main/java/com/stackify/deepjsf/UserControllerBean.java create mode 100644 guest/deep-jsf/src/main/webapp/WEB-INF/faces-config.xml create mode 100644 guest/deep-jsf/src/main/webapp/greet.xhtml create mode 100644 guest/deep-jsf/src/main/webapp/hello.xhtml create mode 100644 guest/deep-jsf/src/main/webapp/index.xhtml create mode 100644 guest/deep-jsf/src/main/webapp/register.xhtml 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From 3a5804aaa846d2f41501eab538d3f7fa6ed1676c Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 18 Jan 2018 12:54:55 +0100 Subject: [PATCH 031/118] Revert "Code for Alessio Stalla's evaluation article (Different Types of Bean Injection in Spring)" This reverts commit c174946f855ff445c787c8ddac683b73e26a0378. --- .../baeldung/typesofbeaninjection/Config.java | 16 ----- .../typesofbeaninjection/domain/Car.java | 24 -------- .../typesofbeaninjection/domain/Engine.java | 24 -------- .../domain/autowired/constructor/Car.java | 30 ---------- .../domain/autowired/properties/Car.java | 30 ---------- .../typesofbeaninjection-context.xml | 19 ------ .../TypeOfBeanInjectionUnitTest.java | 58 ------------------- 7 files changed, 201 deletions(-) delete mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java delete mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java delete mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java delete mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java delete mode 100644 spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java delete mode 100644 spring-core/src/main/resources/typesofbeaninjection-context.xml delete mode 100644 spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java deleted file mode 100644 index a967a63005..0000000000 --- a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/Config.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung.typesofbeaninjection; - -import com.baeldung.typesofbeaninjection.domain.Engine; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan("com.baeldung.typesofbeaninjection") -public class Config { - - @Bean - public Engine engine() { - return new Engine("V8", 5); - } -} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java deleted file mode 100644 index 878f369c8e..0000000000 --- a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Car.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.baeldung.typesofbeaninjection.domain; - -public class Car { - private Engine engine; - - public Car() {} - - public Car(Engine engine) { - this.engine = engine; - } - - public Engine getEngine() { - return engine; - } - - public void setEngine(Engine engine) { - this.engine = engine; - } - - @Override - public String toString() { - return String.format("Car with %s engine", engine); - } -} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java deleted file mode 100644 index 79e7a9856d..0000000000 --- a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/Engine.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.baeldung.typesofbeaninjection.domain; - -public class Engine { - private final String type; - private final int volume; - - public Engine(String type, int volume) { - this.type = type; - this.volume = volume; - } - - public String getType() { - return type; - } - - public int getVolume() { - return volume; - } - - @Override - public String toString() { - return String.format("%s %d", type, volume); - } -} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java deleted file mode 100644 index 33fadf8d2b..0000000000 --- a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/constructor/Car.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.baeldung.typesofbeaninjection.domain.autowired.constructor; - -import com.baeldung.typesofbeaninjection.domain.Engine; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component("car-autowired-by-constructor") -public class Car { - private Engine engine; - - public Car() {} - - @Autowired - public Car(Engine engine) { - this.engine = engine; - } - - public Engine getEngine() { - return engine; - } - - public void setEngine(Engine engine) { - this.engine = engine; - } - - @Override - public String toString() { - return String.format("Car with %s engine", engine); - } -} diff --git a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java b/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java deleted file mode 100644 index 1df33f5062..0000000000 --- a/spring-core/src/main/java/com/baeldung/typesofbeaninjection/domain/autowired/properties/Car.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.baeldung.typesofbeaninjection.domain.autowired.properties; - -import com.baeldung.typesofbeaninjection.domain.Engine; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component("car-autowired-by-properties") -public class Car { - private Engine engine; - - public Car() {} - - public Car(Engine engine) { - this.engine = engine; - } - - public Engine getEngine() { - return engine; - } - - @Autowired - public void setEngine(Engine engine) { - this.engine = engine; - } - - @Override - public String toString() { - return String.format("Car with %s engine", engine); - } -} diff --git a/spring-core/src/main/resources/typesofbeaninjection-context.xml b/spring-core/src/main/resources/typesofbeaninjection-context.xml deleted file mode 100644 index 5a9a0b59d7..0000000000 --- a/spring-core/src/main/resources/typesofbeaninjection-context.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java b/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java deleted file mode 100644 index 1b1319e001..0000000000 --- a/spring-core/src/test/java/com/baeldung/typesofbeaninjection/TypeOfBeanInjectionUnitTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.baeldung.typesofbeaninjection; - -import com.baeldung.typesofbeaninjection.domain.Car; -import com.baeldung.typesofbeaninjection.domain.Engine; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class TypeOfBeanInjectionUnitTest { - - @Test - public void whenConstructionInjectionInXML_thenCarHasEngine() { - ApplicationContext applicationContext - = new ClassPathXmlApplicationContext("/typesofbeaninjection-context.xml"); - Car car = applicationContext.getBean("alices-car", Car.class); - - checkEngine(car.getEngine()); - } - - @Test - public void whenPropertyInjectionInXML_thenCarHasEngine() { - ApplicationContext applicationContext - = new ClassPathXmlApplicationContext("/typesofbeaninjection-context.xml"); - Car car = applicationContext.getBean("bobs-car", Car.class); - - checkEngine(car.getEngine()); - } - - @Test - public void whenConstructionInjectionAnnotations_thenCarHasEngine() { - ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); - com.baeldung.typesofbeaninjection.domain.autowired.constructor.Car car - = applicationContext.getBean(com.baeldung.typesofbeaninjection.domain.autowired.constructor.Car.class); - - checkEngine(car.getEngine()); - } - - @Test - public void whenPropertyInjectionAnnotations_thenCarHasEngine() { - ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); - com.baeldung.typesofbeaninjection.domain.autowired.properties.Car car - = applicationContext.getBean(com.baeldung.typesofbeaninjection.domain.autowired.properties.Car.class); - - checkEngine(car.getEngine()); - } - - private void checkEngine(Engine engine) { - assertNotNull(engine); - assertEquals("V8", engine.getType()); - assertEquals(5, engine.getVolume()); - } - - -} From fe1105a526a2bc3326244dc00004936034e63b3f Mon Sep 17 00:00:00 2001 From: felipeazv Date: Thu, 18 Jan 2018 13:04:56 +0100 Subject: [PATCH 032/118] change module --- spring-5-reactive/pom.xml | 8 +- .../baeldung/reactive/websocket}/Event.java | 2 +- .../ReactiveJavaClientWebSocket.java | 23 +++++ .../ReactiveWebSocketApplication.java | 11 +++ .../ReactiveWebSocketConfiguration.java | 2 +- .../websocket}/ReactiveWebSocketHandler.java | 2 +- .../resources/static/client-websocket.html | 0 spring-reactive-websocket/pom.xml | 92 ------------------- .../ReactiveWebSocketApplication.java | 38 -------- 9 files changed, 44 insertions(+), 134 deletions(-) rename {spring-reactive-websocket/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive/websocket}/Event.java (79%) create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java rename {spring-reactive-websocket/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive/websocket}/ReactiveWebSocketConfiguration.java (96%) rename {spring-reactive-websocket/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive/websocket}/ReactiveWebSocketHandler.java (98%) rename {spring-reactive-websocket => spring-5-reactive}/src/main/resources/static/client-websocket.html (100%) delete mode 100644 spring-reactive-websocket/pom.xml delete mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java 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..3e25e2a29c --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java @@ -0,0 +1,23 @@ +package com.baeldung.reactive.websocket; + +import java.net.URI; +import java.time.Duration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.reactive.socket.WebSocketMessage; +import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; +import org.springframework.web.reactive.socket.client.WebSocketClient; + +import reactor.core.publisher.Mono; + +@SpringBootApplication +public class ReactiveJavaClientWebSocket { + public static void main(String[] args) throws InterruptedException { + 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(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-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java similarity index 98% rename from spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java index 4a548322b3..7f74e714f6 100644 --- a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.reactive.websocket; import org.springframework.web.reactive.socket.WebSocketSession; 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-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 - }; - } -} From a25ea6372a080c56cc49cc2ea0be73a7a801c605 Mon Sep 17 00:00:00 2001 From: felipeazv Date: Thu, 18 Jan 2018 13:40:33 +0100 Subject: [PATCH 033/118] moving module --- .../reactive/websocket/ReactiveJavaClientWebSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 3e25e2a29c..74e2d7daca 100644 --- 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 @@ -13,7 +13,7 @@ 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-me-from-spring-reactive-client"))) + 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()) From 369ec4e181bee2df90e96b4edac5b26a6c6cc04b Mon Sep 17 00:00:00 2001 From: Adi Date: Fri, 19 Jan 2018 00:06:57 +0200 Subject: [PATCH 034/118] BAEL-1267: programmatically create, configure and run a tomcat server --- libraries/pom.xml | 33 ++++++++++ .../java/com/baeldung/tomcat/MyFilter.java | 31 +++++++++ .../java/com/baeldung/tomcat/MyServlet.java | 25 ++++++++ .../baeldung/tomcat/ProgrammaticTomcat.java | 63 +++++++++++++++++++ .../tomcat/ProgrammaticTomcatTest.java | 62 ++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 libraries/src/main/java/com/baeldung/tomcat/MyFilter.java create mode 100644 libraries/src/main/java/com/baeldung/tomcat/MyServlet.java create mode 100644 libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java create mode 100644 libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java diff --git a/libraries/pom.xml b/libraries/pom.xml index 09c8cb8335..404c8fa397 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -705,6 +705,38 @@ test test + + + + org.apache.tomcat + tomcat-catalina + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + + org.apache.tomcat + tomcat-jasper + ${tomcat.version} + + + org.apache.tomcat + tomcat-jasper-el + ${tomcat.version} + + + org.apache.tomcat + tomcat-jsp-api + ${tomcat.version} + @@ -786,5 +818,6 @@ v4-rev493-1.21.0 1.0.0 3.0.14 + 8.5.24
\ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java b/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java new file mode 100644 index 0000000000..9cf4a0ed95 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java @@ -0,0 +1,31 @@ +package com.baeldung.tomcat; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by adi on 1/14/18. + */ +@WebFilter(urlPatterns = "/my-servlet/*") +public class MyFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + System.out.println("Filtering stuff..."); + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.addHeader("myHeader", "myHeaderValue"); + chain.doFilter(request, httpResponse); + } + + @Override + public void destroy() { + + } +} diff --git a/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java b/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java new file mode 100644 index 0000000000..4bbf3c03a7 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java @@ -0,0 +1,25 @@ +package com.baeldung.tomcat; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by adi on 1/10/18. + */ +@WebServlet( + name = "com.baeldung.tomcat.programmatic.MyServlet", + urlPatterns = {"/my-servlet"} +) +public class MyServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().write("test"); + resp.getWriter().flush(); + resp.getWriter().close(); + } +} diff --git a/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java b/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java new file mode 100644 index 0000000000..b84b6b5c6d --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java @@ -0,0 +1,63 @@ +package com.baeldung.tomcat; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; + +import java.io.File; + +/** + * Created by adi on 1/10/18. + */ +public class ProgrammaticTomcat { + + private Tomcat tomcat = null; + + //uncomment for live test + // public static void main(String[] args) throws LifecycleException, ServletException, URISyntaxException, IOException { + // startTomcat(); + // } + + public void startTomcat() throws LifecycleException { + tomcat = new Tomcat(); + tomcat.setPort(8080); + tomcat.setHostname("localhost"); + String appBase = "."; + tomcat + .getHost() + .setAppBase(appBase); + + File docBase = new File(System.getProperty("java.io.tmpdir")); + Context context = tomcat.addContext("", docBase.getAbsolutePath()); + + //add a servlet + Class servletClass = MyServlet.class; + Tomcat.addServlet(context, servletClass.getSimpleName(), servletClass.getName()); + context.addServletMappingDecoded("/my-servlet/*", servletClass.getSimpleName()); + + //add a filter and filterMapping + Class filterClass = MyFilter.class; + FilterDef myFilterDef = new FilterDef(); + myFilterDef.setFilterClass(filterClass.getName()); + myFilterDef.setFilterName(filterClass.getSimpleName()); + context.addFilterDef(myFilterDef); + + FilterMap myFilterMap = new FilterMap(); + myFilterMap.setFilterName(filterClass.getSimpleName()); + myFilterMap.addURLPattern("/my-servlet/*"); + context.addFilterMap(myFilterMap); + + tomcat.start(); + //uncomment for live test + // tomcat + // .getServer() + // .await(); + } + + public void stopTomcat() throws LifecycleException { + tomcat.stop(); + tomcat.destroy(); + } +} diff --git a/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java b/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java new file mode 100644 index 0000000000..d559c3d408 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java @@ -0,0 +1,62 @@ +package com.baeldung.tomcat; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Created by adi on 1/14/18. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class ProgrammaticTomcatTest { + + private ProgrammaticTomcat tomcat = new ProgrammaticTomcat(); + + @Before + public void setUp() throws Exception { + tomcat.startTomcat(); + } + + @After + public void tearDown() throws Exception { + tomcat.stopTomcat(); + } + + @Test + public void givenTomcatStarted_whenAccessServlet_responseIsTestAndResponseHeaderIsSet() throws Exception { + CloseableHttpClient httpClient = HttpClientBuilder + .create() + .build(); + HttpGet getServlet = new HttpGet("http://localhost:8080/my-servlet"); + + HttpResponse response = httpClient.execute(getServlet); + assertEquals(HttpStatus.SC_OK, response + .getStatusLine() + .getStatusCode()); + + String myHeaderValue = response + .getFirstHeader("myHeader") + .getValue(); + assertEquals("myHeaderValue", myHeaderValue); + + HttpEntity responseEntity = response.getEntity(); + assertNotNull(responseEntity); + + String responseString = EntityUtils.toString(responseEntity, "UTF-8"); + assertEquals("test", responseString); + } + +} From 16fe0c8eca150e8d6d2abe8984d069f326176021 Mon Sep 17 00:00:00 2001 From: Dassi orleando Date: Thu, 18 Jan 2018 23:42:48 +0100 Subject: [PATCH 035/118] BAEL-976: Update spring rest article (#3455) * BAEL-1216: improve tests * BAEL-1448: Update Spring 5 articles to use the release version * Setting up the Maven Wrapper on a maven project * Add Maven Wrapper on spring-boot module * simple add * BAEL-976: Update spring version --- spring-rest-embedded-tomcat/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 From b6f6e9dbc41ac9e07a744cfa510985b6f2f6dec9 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 19 Jan 2018 10:02:27 +0200 Subject: [PATCH 036/118] exclude metrics until tests are fixed --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 9bf12725c0..6321b0333e 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,9 @@ lombok mapstruct + mesos-marathon testing-modules/mockito testing-modules/mockito-2 From 7e7ccc7eb391bf9ac2b99f904a5c90a4a15abff4 Mon Sep 17 00:00:00 2001 From: aietcn Date: Sat, 20 Jan 2018 17:09:55 +0800 Subject: [PATCH 037/118] BAEL-1416 quick guide to kong (#3448) * BAEL-1416 quick guide to kong * refactor kong samples --- .../com/baeldung/kong/QueryController.java | 32 ++++ .../main/java/com/baeldung/kong/StockApp.java | 13 ++ .../src/main/resources/application.properties | 2 +- .../baeldung/kong/KongAdminAPILiveTest.java | 165 ++++++++++++++++++ .../kong/KongLoadBalanceLiveTest.java | 68 ++++++++ .../com/baeldung/kong/domain/APIObject.java | 54 ++++++ .../baeldung/kong/domain/ConsumerObject.java | 35 ++++ .../baeldung/kong/domain/KeyAuthObject.java | 21 +++ .../baeldung/kong/domain/PluginObject.java | 30 ++++ .../baeldung/kong/domain/TargetObject.java | 31 ++++ .../baeldung/kong/domain/UpstreamObject.java | 21 +++ 11 files changed, 471 insertions(+), 1 deletion(-) create mode 100644 spring-boot/src/main/java/com/baeldung/kong/QueryController.java create mode 100644 spring-boot/src/main/java/com/baeldung/kong/StockApp.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java 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/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/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; + } +} From 0293c65f7744e95b4fb9937be61fd8c35293960e Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 11:54:24 +0100 Subject: [PATCH 038/118] Trie Examples (#3466) * Implement Trie * Implement TrieTest * Delete testDelete method * Refactorings * Refactor Trie * Refactor TrieNode * Refactor Trie --- .../src/main/java/com/baeldung/trie/Trie.java | 76 +++++++++++++++++++ .../main/java/com/baeldung/trie/TrieNode.java | 31 ++++++++ .../test/java/com/baeldung/trie/TrieTest.java | 68 +++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/trie/Trie.java create mode 100644 core-java/src/main/java/com/baeldung/trie/TrieNode.java create mode 100644 core-java/src/test/java/com/baeldung/trie/TrieTest.java 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..2c4119df71 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/trie/Trie.java @@ -0,0 +1,76 @@ +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++) { + char ch = word.charAt(i); + TrieNode node = current.getChildren() + .get(ch); + if (node == null) { + node = new TrieNode(); + current.getChildren() + .put(ch, node); + } + current = node; + } + current.setEndOfWord(true); + } + + public boolean find(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 void delete(String word) { + delete(root, word, 0); + } + + private boolean delete(TrieNode current, String word, int index) { + if (index == word.length()) { + if (!current.isEndOfWord()) { + return false; + } + current.setEndOfWord(false); + return current.getChildren() + .size() == 0; + } + 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; + } + + public boolean containsNode(String word) { + return find(word); + } + + public boolean isEmpty() { + return root == null; + } +} \ 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/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; + } +} From b1520c93fd73be57276c419a37c2713de6a1c974 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 11:54:39 +0100 Subject: [PATCH 039/118] Update README.md (#3443) --- core-java-concurrency/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-java-concurrency/README.md b/core-java-concurrency/README.md index dc048495e4..8122c71bcb 100644 --- a/core-java-concurrency/README.md +++ b/core-java-concurrency/README.md @@ -33,4 +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) -- [How to Wait for Threads to Finish in the ExecutorService](http://www.baeldung.com/java-executor-wait-for-threads) +- [ExecutorService - Waiting for Threads to Finish](http://www.baeldung.com/java-executor-wait-for-threads) From d64468eddcb36a88c57fff01b2c09029ef27d762 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 13:33:02 +0100 Subject: [PATCH 040/118] Refactor elastic (#3467) --- .../com/baeldung/elasticsearch/Person.java | 3 +- .../ElasticSearchManualTest.java | 19 +-- .../elasticsearch/GeoQueriesTest.java | 150 +++++++++--------- .../data/es/ElasticSearchIntegrationTest.java | 42 ++--- .../es/ElasticSearchQueryIntegrationTest.java | 83 ++++++---- 5 files changed, 153 insertions(+), 144 deletions(-) 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/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..be31de724d 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) @@ -72,21 +71,24 @@ 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("John Smith", new PageRequest(0, 10)); + final Page
articleByAuthorName = articleService + .findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10)); assertEquals(3L, 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 +114,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 +127,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..7a9ac2de06 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,7 +184,9 @@ 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()); From 99040c49f15daf4ee416094db71d783d47bf112a Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 20 Jan 2018 15:27:28 +0200 Subject: [PATCH 041/118] remove gson --- spring-cloud/spring-cloud-security/personresource/pom.xml | 4 ---- .../java/com/baeldung/controller/PersonInfoController.java | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/spring-cloud/spring-cloud-security/personresource/pom.xml b/spring-cloud/spring-cloud-security/personresource/pom.xml index 783fd41e66..ca1ff82515 100644 --- a/spring-cloud/spring-cloud-security/personresource/pom.xml +++ b/spring-cloud/spring-cloud-security/personresource/pom.xml @@ -43,10 +43,6 @@ org.springframework.security spring-security-jwt - - com.google.code.gson - gson - 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 index 15ffc557fc..f2beec927d 100644 --- 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 @@ -14,8 +14,6 @@ public class PersonInfoController { @GetMapping("/personResource") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") public @ResponseBody String personInfo() { - Gson gson = new Gson(); - String person = gson.toJson(new Person("abir", "Dhaka", "Bangladesh", 29, "Male")); - return person; + return new Person("abir", "Dhaka", "Bangladesh", 29, "Male"); } } \ No newline at end of file From 43b26b0ef93a7a91a806a6625e2c614f1e0bb870 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 20 Jan 2018 15:30:28 +0200 Subject: [PATCH 042/118] remove gson --- .../java/com/baeldung/controller/PersonInfoController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 index f2beec927d..9e5420da5a 100644 --- 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 @@ -6,14 +6,13 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.baeldung.model.Person; -import com.google.gson.Gson; @RestController public class PersonInfoController { @GetMapping("/personResource") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") - public @ResponseBody String personInfo() { + public @ResponseBody Person personInfo() { return new Person("abir", "Dhaka", "Bangladesh", 29, "Male"); } } \ No newline at end of file From 7bd1fbf7349426fe730c8f483a2f996399736d7d Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 15:05:41 +0100 Subject: [PATCH 043/118] Refactor Trie (#3469) --- .../src/main/java/com/baeldung/trie/Trie.java | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/core-java/src/main/java/com/baeldung/trie/Trie.java b/core-java/src/main/java/com/baeldung/trie/Trie.java index 2c4119df71..dd51d97b2d 100644 --- a/core-java/src/main/java/com/baeldung/trie/Trie.java +++ b/core-java/src/main/java/com/baeldung/trie/Trie.java @@ -9,26 +9,23 @@ public class Trie { public void insert(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) { - node = new TrieNode(); - current.getChildren() - .put(ch, node); - } - current = node; + current = current.getChildren().computeIfAbsent(word.charAt(i), c -> new TrieNode()); } current.setEndOfWord(true); } - public boolean find(String word) { + 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); + TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } @@ -37,8 +34,8 @@ public class Trie { return current.isEndOfWord(); } - public void delete(String word) { - delete(root, word, 0); + public boolean isEmpty() { + return root == null; } private boolean delete(TrieNode current, String word, int index) { @@ -47,30 +44,19 @@ public class Trie { return false; } current.setEndOfWord(false); - return current.getChildren() - .size() == 0; + return current.getChildren().isEmpty(); } char ch = word.charAt(index); - TrieNode node = current.getChildren() - .get(ch); + TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } boolean shouldDeleteCurrentNode = delete(node, word, index + 1); if (shouldDeleteCurrentNode) { - current.getChildren() - .remove(ch); + current.getChildren().remove(ch); return current.getChildren().isEmpty(); } return false; } - - public boolean containsNode(String word) { - return find(word); - } - - public boolean isEmpty() { - return root == null; - } } \ No newline at end of file From 740073b6ad52550ee0f799a913d46f9ba5d17d94 Mon Sep 17 00:00:00 2001 From: Miguel Rivero Date: Sat, 20 Jan 2018 16:35:48 +0100 Subject: [PATCH 044/118] BAEL-1230: Get Java Logs into the JSON Format (#3381) * Added code for BAEL-1230: Getting log in JSON format * Added code for BAEL-1230: Getting log in JSON format * Improved tests to check correct JSON format in logs * Renamed Test classes to make clear what they do * Fixed wrong indentation --- logging-modules/log4j2/pom.xml | 4 +- .../logging/log4j2/tests/JSONLayoutTest.java | 45 ++++++++++++++ .../log4j2/src/test/resources/log4j2.xml | 62 ++++++++++++------- logging-modules/logback/pom.xml | 20 +++++- .../com/baeldung/logback/JSONLayoutTest.java | 45 ++++++++++++++ .../src/test/resources/logback-test.xml | 25 ++++++-- 6 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java create mode 100644 logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java 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 From 6923d3accd1a9c4a235e1dc1fa73281c0227d35a Mon Sep 17 00:00:00 2001 From: Seun Matt Date: Sat, 20 Jan 2018 18:18:01 +0100 Subject: [PATCH 045/118] Example Code for Exceptions in Netty (#3406) * added updated example codes * updated example code StringToCharStream * deleted StringToCharStream.java locally * removed redundant file * added code for apache commons collection SetUtils * refactored example code * added example code for bytebuddy * added example code for PCollections * update pom * refactored tests for PCollections * spring security xml config * spring security xml config * remove redundant comment * example code for apache-shiro * updated example code for Vavr Collections * updated Vavr's Collection example * updated Vavr Collection file * updated example code for Apache Shiro * updated Vavr Collections example * added example code for N1QL * update example code for N1QL * added integration test for N1QL * update N1QL Example code * update the N1QL example Code * rename module to couchbase * rename module to couchbase * change module name in parent module and pom * added cas-server module * added cas secured app for Spring SSO with CAS * refactor cas modules into cas folder * updated files * removed redundant files * refactor the config for cas-server * added sql file to generate tables and database * added source code for VRaptor * update source code for VRaptor article * update pom and reformat code * add example code for BAEL-1439 * updated code for netty exceptions --- .../com/baeldung/netty/ChannelHandlerA.java | 22 +++++++++ .../com/baeldung/netty/ChannelHandlerB.java | 20 ++++++++ .../java/com/baeldung/netty/NettyServerB.java | 47 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java create mode 100644 libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java create mode 100644 libraries/src/main/java/com/baeldung/netty/NettyServerB.java 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(); + } +} From 30321eb8018ef6ced42df32e1532e38ef87a88c2 Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Sun, 21 Jan 2018 23:37:11 +0700 Subject: [PATCH 046/118] initial commit for the BAEL-1479 --- .../baeldung/finalize/CloseableResource.java | 30 +++++++++++++++++++ .../com/baeldung/finalize/Finalizable.java | 30 +++++++++++++++++++ .../baeldung/finalize/FinalizeUnitTest.java | 23 ++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/finalize/CloseableResource.java create mode 100644 core-java/src/main/java/com/baeldung/finalize/Finalizable.java create mode 100644 core-java/src/test/java/com/baeldung/finalize/FinalizeUnitTest.java 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/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); + } + } +} From 25f449ad38749311959d7c82cc1f89e82a8450e7 Mon Sep 17 00:00:00 2001 From: Ahmad Alsanie Date: Sun, 21 Jan 2018 19:44:22 +0200 Subject: [PATCH 047/118] BAEL-1473 (#3477) * BAEL-1473 Intoduction to Spliterator in Java * BAEL-1473 - Replace .out with logger.info * removed log * BAEL-1473 - added test-cases * modify test-cases --- .../com/baeldung/spliteratorAPI/Executor.java | 44 ++++--------------- .../com/baeldung/spliteratorAPI/Task.java | 9 ++-- .../baeldung/spliteratorAPI/ExecutorTest.java | 44 +++++++++++++++++++ 3 files changed, 58 insertions(+), 39 deletions(-) create mode 100644 core-java-8/src/test/java/com/baeldung/spliteratorAPI/ExecutorTest.java 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/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 + ""); + } +} From 7000fca1971ce2c87f7d5222cd5b3b786033ceec Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Mon, 22 Jan 2018 14:30:58 +0100 Subject: [PATCH 048/118] BAEL-1398 Code for the article: HTTP Requests with Kotlin and khttp --- core-kotlin/pom.xml | 5 + .../com/baeldung/kotlin/khttp/KhttpTest.kt | 153 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/khttp/KhttpTest.kt 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 From b1266c833b4a458bfa0c09cd0604013ba60170d8 Mon Sep 17 00:00:00 2001 From: Juan Moreno Date: Mon, 22 Jan 2018 12:08:33 -0300 Subject: [PATCH 049/118] BAEL-1148 earth001@gmail.com - Merged spring-mvc-push module in spring-mvc-simple (#3459) * Sample code for BAEL-1148 - earth001@gmail.com * Change tabs for spaces in non java files * Change tabs for spaces in non java files * Removed unnecessary argument * BAEL-1148 earth001@gmail.com - Mapped get method in controller / Removed resources * Merged spring-mvc-push module in spring-mvc-simple --- spring-mvc-push/.gitignore | 1 - spring-mvc-push/pom.xml | 91 ----------------- .../baeldung/config/PushConfiguration.java | 48 --------- spring-mvc-push/src/main/webapp/index.jsp | 14 --- .../src/main/webapp/resources/script.js | 1 - .../src/main/webapp/resources/style.css | 9 -- spring-mvc-simple/pom.xml | 93 +++++++++--------- .../ApplicationConfiguration.java | 4 +- .../JadeTemplateConfiguration.java | 22 ++--- .../configuration/PushConfiguration.java | 30 ++++++ .../spring/configuration/WebInitializer.java | 49 +++------ ...AnnotationMethodHandlerAdapterExample.java | 12 +-- .../controller/FileUploadController.java | 18 ++-- .../RequestMappingHandlerAdapterExample.java | 12 +-- ...SimpleControllerHandlerAdapterExample.java | 16 ++- .../controller/push}/PushController.java | 2 +- ...servlet_AnnotationMethodHandlerAdapter.xml | 26 ----- ...g-servlet_RequestMappingHandlerAdapter.xml | 28 ------ ...servlet_SimpleControllerHandlerAdapter.xml | 25 ----- .../src/main/webapp/WEB-INF/views/demo.jsp | 3 +- .../WEB-INF/views/registration-jade.jade | 2 +- .../src/main/webapp/resources/logo.png | Bin .../push}/PushControllerIntegrationTest.java | 6 +- 23 files changed, 138 insertions(+), 374 deletions(-) delete mode 100644 spring-mvc-push/.gitignore delete mode 100644 spring-mvc-push/pom.xml delete mode 100644 spring-mvc-push/src/main/java/com/baeldung/config/PushConfiguration.java delete mode 100644 spring-mvc-push/src/main/webapp/index.jsp delete mode 100644 spring-mvc-push/src/main/webapp/resources/script.js delete mode 100644 spring-mvc-push/src/main/webapp/resources/style.css create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/PushConfiguration.java rename {spring-mvc-push/src/main/java/com/baeldung/controller => spring-mvc-simple/src/main/java/com/baeldung/spring/controller/push}/PushController.java (92%) delete mode 100644 spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml delete mode 100644 spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml delete mode 100644 spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml rename {spring-mvc-push => spring-mvc-simple}/src/main/webapp/WEB-INF/views/demo.jsp (87%) rename {spring-mvc-push => spring-mvc-simple}/src/main/webapp/resources/logo.png (100%) rename {spring-mvc-push/src/test/java/com/baeldung/controller => spring-mvc-simple/src/test/java/com/baeldung/controller/push}/PushControllerIntegrationTest.java (90%) 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/pom.xml b/spring-mvc-simple/pom.xml index 2cc6aec906..05b2eb49b6 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -8,83 +8,62 @@ 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 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 +94,7 @@ groovy-templates ${groovy.version} - + de.neuland-bfi @@ -123,6 +102,19 @@ ${jade.version} + + + org.springframework + spring-test + ${springframework.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + @@ -141,11 +133,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/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 From 692f12636f79be0be58fd86b6d527ba28be690f6 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Mon, 22 Jan 2018 18:46:11 +0200 Subject: [PATCH 050/118] add pact provider test (#3475) --- .../PactConsumerDrivenContractUnitTest.java | 12 ++-- spring-rest/pom.xml | 8 +++ .../web/controller/PactController.java | 32 ++++++++++ .../java/org/baeldung/web/dto/PactDto.java | 33 ++++++++++ .../pacts/test_consumer-test_provider.json | 61 +++++++++++++++++++ .../org/baeldung/pact/PactProviderTest.java | 40 ++++++++++++ 6 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 spring-rest/src/main/java/org/baeldung/web/controller/PactController.java create mode 100644 spring-rest/src/main/java/org/baeldung/web/dto/PactDto.java create mode 100644 spring-rest/src/main/resources/pacts/test_consumer-test_provider.json create mode 100644 spring-rest/src/test/java/org/baeldung/pact/PactProviderTest.java 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/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() { + } + +} From 3e5ac1de94aa9c139ecc433898265bc29dd69abe Mon Sep 17 00:00:00 2001 From: felipeazv Date: Mon, 22 Jan 2018 22:09:57 +0100 Subject: [PATCH 051/118] BAEL-1475: code formatting --- .../ReactiveJavaClientWebSocket.java | 15 ++++---- .../websocket/ReactiveWebSocketHandler.java | 35 +++++-------------- 2 files changed, 18 insertions(+), 32 deletions(-) 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 index 74e2d7daca..c9a333c044 100644 --- 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 @@ -2,22 +2,25 @@ package com.baeldung.reactive.websocket; import java.net.URI; import java.time.Duration; -import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.reactive.socket.WebSocketMessage; import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; import org.springframework.web.reactive.socket.client.WebSocketClient; import reactor.core.publisher.Mono; -@SpringBootApplication public class ReactiveJavaClientWebSocket { - public static void main(String[] args) throws InterruptedException { + public static void main(String[] args) throws InterruptedException { + WebSocketClient client = new ReactorNettyWebSocketClient(); - client.execute(URI.create("ws://localhost:8080/event-emitter"), session -> session.send(Mono.just(session.textMessage("event-me-from-spring-reactive-client"))) + client.execute( + URI.create("ws://localhost:8080/event-emitter"), + session -> session.send( + Mono.just(session.textMessage("event-spring-reactive-client-websocket"))) .thenMany(session.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()) + .map(WebSocketMessage::getPayloadAsText) + .log()) .then()) .block(Duration.ofSeconds(10L)); } + } 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 index 7f74e714f6..eea0fb9962 100644 --- 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 @@ -12,7 +12,6 @@ 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; @@ -20,27 +19,14 @@ import java.util.UUID; @Component public class ReactiveWebSocketHandler implements WebSocketHandler { - private Flux eventFlux; - private Flux intervalFlux; + private Flux eventFlux = Flux.generate(e -> { + Event event = new Event(UUID.randomUUID().toString(), LocalDateTime.now().toString()); + e.next(event); + }); - /** - * Here we prepare a Flux that will emit a message every second - */ - @PostConstruct - private void init() throws InterruptedException { + private Flux intervalFlux = Flux.interval(Duration.ofMillis(1000L)).zipWith(eventFlux, (time, event) -> event); - 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); - - } + private ObjectMapper json = new ObjectMapper(); /** * On each new client session, send the message flux to the client. @@ -50,7 +36,7 @@ public class ReactiveWebSocketHandler implements WebSocketHandler { */ @Override public Mono handle(WebSocketSession webSocketSession) { - ObjectMapper json = new ObjectMapper(); + return webSocketSession.send(intervalFlux.map(event -> { try { String jsonEvent = json.writeValueAsString(event); @@ -60,12 +46,9 @@ public class ReactiveWebSocketHandler implements WebSocketHandler { e.printStackTrace(); return ""; } - }) - .map(webSocketSession::textMessage)) + }).map(webSocketSession::textMessage)) - .and(webSocketSession.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()); + .and(webSocketSession.receive().map(WebSocketMessage::getPayloadAsText).log()); } } From c38de161c68e0b327b5f27e26ed01d328882a8f3 Mon Sep 17 00:00:00 2001 From: felipeazv Date: Mon, 22 Jan 2018 22:28:02 +0100 Subject: [PATCH 052/118] removing verbose comment --- .../reactive/websocket/ReactiveWebSocketHandler.java | 6 ------ 1 file changed, 6 deletions(-) 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 index eea0fb9962..669c212fd3 100644 --- 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 @@ -28,12 +28,6 @@ public class ReactiveWebSocketHandler implements WebSocketHandler { private ObjectMapper json = new ObjectMapper(); - /** - * 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) { From db903618f3f55d93c9d6de07266834e83573524f Mon Sep 17 00:00:00 2001 From: Donato Rimenti Date: Tue, 23 Jan 2018 00:28:45 +0100 Subject: [PATCH 053/118] BAEL-1321 Jetty 9 Server - Create, Configure programmatically (#3482) * Implemented server factory and its tests. Changed Jetty version to latest, removed duplicate entry and added jetty-webapp dependency in pom.xml. * Added missing war file and changed tests port for Travis build. --- libraries/pom.xml | 8 +- .../baeldung/jetty/JettyServerFactory.java | 94 ++++++++++ .../baeldung/jetty/LoggingRequestHandler.java | 168 ++++++++++++++++++ .../jetty/JettyServerFactoryUnitTest.java | 86 +++++++++ .../resources/jetty-embedded-demo-app.war | Bin 0 -> 1021 bytes 5 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java create mode 100644 libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java create mode 100644 libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java create mode 100644 libraries/src/test/resources/jetty-embedded-demo-app.war 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/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/resources/jetty-embedded-demo-app.war b/libraries/src/test/resources/jetty-embedded-demo-app.war new file mode 100644 index 0000000000000000000000000000000000000000..7f8bc37df0cfd132b96699b37b9f8c12c55cfa4c GIT binary patch literal 1021 zcmWIWW@h1H0D)t(Onkr$D8UJ&eO*Hwbv^yu^aFs(M1TSu3@?Dn4mbN7Xb)7iapDYQR zBBp&nrDEqCFXLjblDS%IMPfdE7V`ci+GJE5{MpN>a_5}xAYt#I-~&Z{teqz%S90y^ zOkG*Yb*$=G)ii;*n!DmOZz`PD`NH$)Y=haSB1Q&~GqL-I1L(|fS0}hH2dg*UFuqD>XG&YI4P`R8^hI)yc}*W%kRV)#2$cgW6vK z&2Z;)FsuSvCC{9mxg2Q4Js{=>Vwm&GQs*^V+=N76OR7#J4(Q7_ngFMb228+z5FWf*$1Is|E+DGcG-)peBJO|IZln$ z{p7KOXO4B<%UKxEqmUNLW`5COQK`D*)}jD`2?B=pmGiT+*WOdKdofwaSo2Vu+|H*v zyk0HfJ2}a=BldXdqOEaE4o8+=l$~4aS}mxqoZUKiYeR|16|R5ldq1S_Kk&i3nmNFm zkx7IZdvJr27z79~ymbVz;3)~DO&*-Muq8Pas5S*4t&Xe>DFJdZfMgjM7#SoOJeNP+ zD?RBl06txy6a)bvU0;Aqm@Y(mf&~cn6a~}2q)~|x kIej5)#hyG6`g(!-V75XNOMo{k8%O~g5VirO6PZ9f0JvWo7ytkO literal 0 HcmV?d00001 From 3459d3ad47d52e41c58431dc4bc0acacc8d73e3b Mon Sep 17 00:00:00 2001 From: chrisoberle Date: Mon, 22 Jan 2018 18:41:03 -0500 Subject: [PATCH 054/118] BAEL-1418 Spring Security with Extra Login Fields (#3476) * BAEL-1418 - spring security with extra login fields * change delimeter for username/domain concatenation * remove unnecessary class * move source to spring-5-security module * finish moving example code to spring-5-security module * fix formatting in pom * adjust spacing * BAEL-1418 Spring Security with Extra Login Fields * added additional custom example * refactored and added tests * remove final keywords and serialVersionUID constants --- .../CustomAuthenticationFilter.java | 50 ++++++++++ .../CustomAuthenticationToken.java | 28 ++++++ ...stomUserDetailsAuthenticationProvider.java | 92 +++++++++++++++++++ .../CustomUserDetailsService.java | 10 ++ .../CustomUserDetailsServiceImpl.java | 30 ++++++ .../CustomUserRepository.java | 2 +- .../ExtraLoginFieldsApplication.java} | 6 +- .../SecurityConfig.java | 62 +++++++++++++ .../User.java | 4 +- .../UserRepository.java | 2 +- .../WebController.java | 2 +- .../ExtraLoginFieldsApplication.java | 13 +++ .../SecurityConfig.java | 6 +- .../SimpleAuthenticationFilter.java} | 4 +- .../SimpleUserDetailsService.java} | 8 +- .../SimpleUserRepository.java | 26 ++++++ .../baeldung/loginextrafieldssimple/User.java | 21 +++++ .../UserRepository.java | 7 ++ .../loginextrafieldssimple/WebController.java | 51 ++++++++++ .../src/main/resources/static/css/main.css | 12 +-- .../resources/templatesextrafields/index.html | 22 +++-- .../resources/templatesextrafields/login.html | 49 ++++++---- .../templatesextrafields/user/index.html | 29 +++--- .../AbstractExtraLoginFieldsTest.java | 46 ++++++++++ .../LoginFieldsFullTest.java} | 45 ++------- .../LoginFieldsSimpleTest.java | 72 +++++++++++++++ 26 files changed, 597 insertions(+), 102 deletions(-) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/CustomUserRepository.java (91%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/SpringExtraLoginFieldsApplication.java => loginextrafieldscustom/ExtraLoginFieldsApplication.java} (54%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/User.java (87%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/UserRepository.java (66%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/WebController.java (93%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldssimple}/SecurityConfig.java (91%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/CustomAuthenticationFilter.java => loginextrafieldssimple/SimpleAuthenticationFilter.java} (92%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/CustomUserDetailsService.java => loginextrafieldssimple/SimpleUserDetailsService.java} (82%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java create mode 100644 spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java rename spring-5-security/src/test/java/com/baeldung/{securityextrafields/SecurityExtraFieldsTest.java => loginextrafields/LoginFieldsFullTest.java} (65%) create mode 100644 spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java 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/securityextrafields/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java index c86769b016..428c8bf532 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.ArrayList; import java.util.Collection; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java similarity index 54% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java index a779acc75e..0cf934f288 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java @@ -1,13 +1,13 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class SpringExtraLoginFieldsApplication { +public class ExtraLoginFieldsApplication { public static void main(String[] args) { - SpringApplication.run(SpringExtraLoginFieldsApplication.class, 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/securityextrafields/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java similarity index 87% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java index a5b3a434ae..aa03f15b6a 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Collection; @@ -8,7 +8,7 @@ public class User extends org.springframework.security.core.userdetails.User { private static final long serialVersionUID = 1L; - private final String domain; + private String domain; public User(String username, String domain, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java similarity index 66% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java index 4ca65b13d5..e2358e055b 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; public interface UserRepository { diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java similarity index 93% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java index 4a8abb4a83..b5e0b511ac 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Optional; 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/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java index 429f6df972..d8c5ea8147 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.PropertySource; @@ -36,8 +36,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .logoutUrl("/logout"); } - public CustomAuthenticationFilter authenticationFilter() throws Exception { - CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); + public SimpleAuthenticationFilter authenticationFilter() throws Exception { + SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setAuthenticationFailureHandler(failureHandler()); return filter; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java similarity index 92% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java index b5d628628d..9dcb524157 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -9,7 +9,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { +public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java similarity index 82% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java index be02834852..2fad50ad01 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.userdetails.UserDetails; @@ -7,11 +7,11 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service("userDetailsService") -public class CustomUserDetailsService implements UserDetailsService { +public class SimpleUserDetailsService implements UserDetailsService { - private final UserRepository userRepository; + private UserRepository userRepository; - public CustomUserDetailsService(UserRepository userRepository) { + public SimpleUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } 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/static/css/main.css b/spring-5-security/src/main/resources/static/css/main.css index 9299ee6158..febc353af7 100644 --- a/spring-5-security/src/main/resources/static/css/main.css +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -1,18 +1,8 @@ -body { - font-family: sans; - font-size: 1em; -} - p.error { font-weight: bold; color: red; } div.logout { - float: right; + margin-right: 2em;; } - -.formfield { - margin: 0.5em; - padding: 0.3em; -} \ No newline at end of file diff --git a/spring-5-security/src/main/resources/templatesextrafields/index.html b/spring-5-security/src/main/resources/templatesextrafields/index.html index 52f6224dfb..37833ff0d2 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -1,24 +1,32 @@ - + - Spring Security - Login With Extra Fields + Spring Security with Extra Fields + + + + + -
- Logged in user: | - domain: Some Domain +
+
+

Logged in: | Some Domain +

- +
-

Hello Spring Security

+ +

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 index cafec89c15..5c51ea3b2c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/login.html +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -1,23 +1,36 @@ - - - Login page - - - - -

Login page

+ + + Login page + + + + + + + + + +
+ +

+ + +

+

+ + +

+

+ + +

+

Back to home 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 index a4c1535100..9c41f0e78c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/user/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -1,13 +1,20 @@ - - - Spring Security - Login With Extra Fields - - - - -
-

This is a secured page!

-

Back to home page

- + + + 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/securityextrafields/SecurityExtraFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java similarity index 65% rename from spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java rename to spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java index cf0701708d..38c219cb5e 100644 --- a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java @@ -1,8 +1,7 @@ -package com.baeldung.securityextrafields; +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.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; @@ -11,57 +10,26 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.ArrayList; import java.util.Collection; -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.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.FilterChainProxy; 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.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.loginextrafieldscustom.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldscustom.User; @RunWith(SpringRunner.class) @SpringJUnitWebConfig -@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class) -public class SecurityExtraFieldsTest { - - @Autowired - private FilterChainProxy springSecurityFilterChain; - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) - .apply(springSecurity(springSecurityFilterChain)).build(); - } - - @Test - public void givenRootPathAccess_thenRedirectToIndex() throws Exception { - this.mockMvc.perform(get("/")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("/index*")); - } - - @Test - public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { - this.mockMvc.perform(get("/user/index")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("**/login")); - } +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsFullTest extends AbstractExtraLoginFieldsTest { @Test public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { @@ -100,4 +68,5 @@ public class SecurityExtraFieldsTest { 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); + } + +} From af3edc477d195e444db28cffbdb9c254bfd0e4a3 Mon Sep 17 00:00:00 2001 From: Dassi orleando Date: Tue, 23 Jan 2018 04:43:24 +0100 Subject: [PATCH 055/118] BAEL-1273: Display RSS feed with spring mvc (AbstractRssFeedView) (#3490) * BAEL-1216: improve tests * BAEL-1448: Update Spring 5 articles to use the release version * Setting up the Maven Wrapper on a maven project * Add Maven Wrapper on spring-boot module * simple add * BAEL-976: Update spring version * BAEL-1273: Display RSS feed with spring mvc (AbstractRssFeedView) --- spring-boot/pom.xml | 7 +++ .../com/baeldung/rss/ArticleFeedView.java | 54 +++++++++++++++++++ .../baeldung/rss/ArticleRssController.java | 16 ++++++ .../com/baeldung/rss/CustomContainer.java | 16 ++++++ .../main/java/com/baeldung/rss/RssApp.java | 20 +++++++ 5 files changed, 113 insertions(+) create mode 100644 spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java create mode 100644 spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java create mode 100644 spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java create mode 100644 spring-boot/src/main/java/com/baeldung/rss/RssApp.java 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/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); + } + +} From 9f1429b067ccbc26204d950e8baa1e653f8f7fdb Mon Sep 17 00:00:00 2001 From: christopherfranklin Date: Tue, 23 Jan 2018 12:29:31 -0500 Subject: [PATCH 056/118] BAEL-1324 A Simple Tagging Implementation with Elasticsearch (#3464) * Christopher Franklin A Simple Tagging Implementation with Elasticsearch Modifying the existing Spring Data Elasticsearch example to use the tags already on the model. Also added a number of tests as examples of how to use the tags. --- .../data/es/repository/ArticleRepository.java | 9 +++++++- .../data/es/service/ArticleService.java | 7 +++++- .../data/es/service/ArticleServiceImpl.java | 15 +++++++++++-- .../data/es/ElasticSearchIntegrationTest.java | 22 +++++++++++++++++-- .../es/ElasticSearchQueryIntegrationTest.java | 12 ++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) 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/spring/data/es/ElasticSearchIntegrationTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java index be31de724d..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 @@ -46,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); } @@ -78,12 +86,22 @@ public class ElasticSearchIntegrationTest { @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() { 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 7a9ac2de06..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 @@ -191,4 +191,16 @@ public class ElasticSearchQueryIntegrationTest { 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()); + } } From 689083a090caf9095d886b21e42f6f1e803614cf Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Wed, 24 Jan 2018 08:12:03 -0600 Subject: [PATCH 057/118] BAEL-1148 README (#3505) * BAEL-973: updated README * BAEL-1069: Updated README * BAEL-817: add README file * BAEL-1084: README update * BAEL-960: Update README * BAEL-1155: updated README * BAEL-1041: updated README * BAEL-973: Updated README * BAEL-1187: updated README * BAEL-1183: Update README * BAEL-1133: Updated README * BAEL-1098: README update * BAEL-719: add README.md * BAEL-1272: README update * BAEL-1272: README update * BAEL-1196: Update README * BAEL-1328: Updated README * BAEL-1371: Update README.md * BAEL-1371: Update README.md * BAEL-1278: Update README * BAEL-1326: Update README * BAEL-399: Update README * BAEL-1297: Update README * BAEL-1218: README * BAEL-1148 README update --- spring-mvc-simple/README.md | 1 + 1 file changed, 1 insertion(+) 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) From 0585764866cc01bba4adb07a0c91d16d6038116b Mon Sep 17 00:00:00 2001 From: Ahmad Alsanie Date: Wed, 24 Jan 2018 19:29:14 +0200 Subject: [PATCH 058/118] BAEL-1473 replaced int with AtomicInteger for safe usage in multi-threaded env (#3497) * BAEL-1473 Intoduction to Spliterator in Java * BAEL-1473 - Replace .out with logger.info * removed log * BAEL-1473 - added test-cases * modify test-cases * AtomicInteger instead of int * SIZED removed --- .../RelatedAuthorSpliterator.java | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) 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; - } } From 70e4a552568f1041667cc1c2276ef07ee26ad9c2 Mon Sep 17 00:00:00 2001 From: Eugen Paraschiv Date: Thu, 25 Jan 2018 11:33:50 +0200 Subject: [PATCH 059/118] minor import cleanup --- .../java/com/baeldung/utils/controller/UtilsController.java | 1 - .../main/java/com/baeldung/webjar/WebjarsdemoApplication.java | 1 - .../org/baeldung/boot/converter/GenericBigDecimalConverter.java | 1 - .../controller/StringToEmployeeConverterController.java | 1 - .../src/main/java/org/baeldung/main/SpringBootApplication.java | 1 - .../test/java/org/baeldung/SpringBootMailIntegrationTest.java | 2 -- 6 files changed, 7 deletions(-) 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/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; From 19e8849361c52cb57793df924d7c419b56eb123e Mon Sep 17 00:00:00 2001 From: gautamshetty Date: Thu, 25 Jan 2018 04:16:39 -0600 Subject: [PATCH 060/118] [BAEL 1209] - Java RMI Files. (#3374) * [BAEL 1209] - Java RMI Files. * Added parent tag and deleted dependency tag for junit. * Added java-rmi module. * Removed duplicate java-lite module entry. * Deleting this file as it is covered in test class. * Spell check. --- java-rmi/pom.xml | 20 +++++++++ .../main/java/com/baeldung/rmi/Message.java | 36 +++++++++++++++ .../com/baeldung/rmi/MessengerService.java | 11 +++++ .../baeldung/rmi/MessengerServiceImpl.java | 37 ++++++++++++++++ .../baeldung/rmi/JavaRMIIntegrationTest.java | 44 +++++++++++++++++++ pom.xml | 5 ++- 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 java-rmi/pom.xml create mode 100644 java-rmi/src/main/java/com/baeldung/rmi/Message.java create mode 100644 java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java create mode 100644 java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java create mode 100644 java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java 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/pom.xml b/pom.xml index 6321b0333e..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 From 211c8a0c30b88c7f5411cae976e3762eb135eaea Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Thu, 25 Jan 2018 22:06:00 +0100 Subject: [PATCH 061/118] added link to finalize article --- core-java/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java/README.md b/core-java/README.md index 6a4a87de0c..ed60db855c 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -126,3 +126,4 @@ - [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces) - [Polymorphism in Java](http://www.baeldung.com/java-polymorphism) - [Recursion In Java](http://www.baeldung.com/java-recursion) +- [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize) From 5d6c47789e359f63cf5c5faddea77ce0e9ce9273 Mon Sep 17 00:00:00 2001 From: ramansahasi Date: Fri, 26 Jan 2018 23:34:38 +0530 Subject: [PATCH 062/118] Logged InterruptedException instead of ignoring it (#3517) --- .../concurrent/waitandnotify/Data.java | 10 ++- .../concurrent/waitandnotify/Receiver.java | 4 +- .../concurrent/waitandnotify/Sender.java | 14 ++-- .../waitandnotify/NetworkIntegrationTest.java | 78 +++++++++---------- 4 files changed, 58 insertions(+), 48 deletions(-) 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()); + } } From 3f3204e14f9e13dcf174c12894ae53d34ae1dac1 Mon Sep 17 00:00:00 2001 From: Adi Date: Fri, 26 Jan 2018 21:18:17 +0200 Subject: [PATCH 063/118] BAEL-1267: programmatically create, configure and run a tomcat server - remove unused dependencies --- libraries/pom.xml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/libraries/pom.xml b/libraries/pom.xml index 404c8fa397..6c0e6c2577 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -712,31 +712,6 @@ tomcat-catalina ${tomcat.version} - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-jasper - ${tomcat.version} - - - org.apache.tomcat - tomcat-jasper - ${tomcat.version} - - - org.apache.tomcat - tomcat-jasper-el - ${tomcat.version} - - - org.apache.tomcat - tomcat-jsp-api - ${tomcat.version} - From fab4aec7a1dbad52dee577ecb83e76594e38bcf4 Mon Sep 17 00:00:00 2001 From: Satish Pandey Date: Sat, 27 Jan 2018 10:54:22 +0530 Subject: [PATCH 064/118] Instance profile credentials example. (#3401) * Instance profile credentials example. * InstanceProfile CloudFormation template included to provision AWS environment. * Included java package installation under Yaml script. * Using SpringApplication reference to prepare application context. * Introducing SpringCloudS3Service to handle all S3 operations. --- spring-cloud/spring-cloud-aws/README.md | 5 ++ spring-cloud/spring-cloud-aws/pom.xml | 1 + .../aws/InstanceProfileAwsApplication.java | 60 ++++++++++++++ .../cloud/aws/s3/SpringCloudS3Service.java | 64 +++++++++++++++ .../resources/InstanceProfileFormation.yaml | 78 +++++++++++++++++++ .../application-instance-profile.properties | 14 ++++ 6 files changed, 222 insertions(+) create mode 100644 spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java create mode 100644 spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java create mode 100644 spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml create mode 100644 spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties 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 From 955a2e7e18c895d1b2580fcc9ef3d955c5d435a1 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 27 Jan 2018 06:52:34 +0100 Subject: [PATCH 065/118] Reactive exception handling --- .../websocket/ReactiveWebSocketHandler.java | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) 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 index 669c212fd3..2e93c0c0dc 100644 --- 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 @@ -1,48 +1,41 @@ package com.baeldung.reactive.websocket; -import org.springframework.web.reactive.socket.WebSocketSession; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - import org.springframework.stereotype.Component; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketMessage; - +import org.springframework.web.reactive.socket.WebSocketSession; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.time.Duration; -import java.time.LocalDateTime; -import java.util.UUID; + +import static java.time.LocalDateTime.now; +import static java.util.UUID.randomUUID; @Component public class ReactiveWebSocketHandler implements WebSocketHandler { - private Flux eventFlux = Flux.generate(e -> { - Event event = new Event(UUID.randomUUID().toString(), LocalDateTime.now().toString()); - e.next(event); + private static final ObjectMapper json = new ObjectMapper(); + + private Flux eventFlux = Flux.generate(sink -> { + Event event = new Event(randomUUID().toString(), now().toString()); + try { + sink.next(json.writeValueAsString(event)); + } catch (JsonProcessingException e) { + sink.error(e); + } }); - private Flux intervalFlux = Flux.interval(Duration.ofMillis(1000L)).zipWith(eventFlux, (time, event) -> event); - - private ObjectMapper json = new ObjectMapper(); + private Flux intervalFlux = Flux.interval(Duration.ofMillis(1000L)) + .zipWith(eventFlux, (time, event) -> event); @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()); + return webSocketSession.send(intervalFlux + .map(webSocketSession::textMessage)) + .and(webSocketSession.receive() + .map(WebSocketMessage::getPayloadAsText).log()); } - } From 81e8da29a402f0cb8fd1c362ce249271b1e94f5d Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Sat, 27 Jan 2018 11:14:14 +0100 Subject: [PATCH 066/118] BAEL-1335 --- javaxval/pom.xml | 15 +- .../MethodValidationConfig.java | 38 ++++ .../ConsistentDateParameterValidator.java | 32 +++ .../constraints/ConsistentDateParameters.java | 23 ++ .../constraints/ValidReservation.java | 24 +++ .../ValidReservationValidator.java | 40 ++++ .../methodvalidation/model/Customer.java | 41 ++++ .../methodvalidation/model/Reservation.java | 60 ++++++ .../model/ReservationManagement.java | 50 +++++ .../ContainerValidationIntegrationTest.java | 86 ++++++++ .../ValidationIntegrationTest.java | 199 ++++++++++++++++++ 11 files changed, 606 insertions(+), 2 deletions(-) create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java create mode 100644 javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java create mode 100644 javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java create mode 100644 javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java diff --git a/javaxval/pom.xml b/javaxval/pom.xml index 6a83a25f01..0891fe6959 100644 --- a/javaxval/pom.xml +++ b/javaxval/pom.xml @@ -6,10 +6,11 @@ 0.1-SNAPSHOT - 2.0.0.Final - 6.0.2.Final + 2.0.1.Final + 6.0.7.Final 3.0.0 2.2.6 + 5.0.2.RELEASE @@ -50,6 +51,16 @@ javax.el ${javax.el.version} + + org.springframework + spring-context + ${org.springframework.version} + + + org.springframework + spring-test + ${org.springframework.version} + diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java new file mode 100644 index 0000000000..206a145337 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java @@ -0,0 +1,38 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; + +import java.time.LocalDate; + +@Configuration +@ComponentScan({ "org.baeldung.javaxval.methodvalidation.model" }) +public class MethodValidationConfig { + + @Bean + public MethodValidationPostProcessor methodValidationPostProcessor() { + return new MethodValidationPostProcessor(); + } + + @Bean("customer") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + public Customer customer(String firstName, String lastName) { + + Customer customer = new Customer(firstName, lastName); + return customer; + } + + @Bean("reservation") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + public Reservation reservation(LocalDate begin, LocalDate end, Customer customer, int room) { + + Reservation reservation = new Reservation(begin, end, customer, room); + return reservation; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java new file mode 100644 index 0000000000..b28abcba45 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java @@ -0,0 +1,32 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; +import java.time.LocalDate; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class ConsistentDateParameterValidator implements ConstraintValidator { + + @Override + public void initialize(ConsistentDateParameters constraintAnnotation) { + } + + @Override + public boolean isValid(Object[] value, ConstraintValidatorContext context) { + if (value.length != 4 && value.length != 3) { + throw new IllegalArgumentException("Illegal method signature"); + } + + if (value[0] == null || value[1] == null) { + return false; + } + + if (!(value[0] instanceof LocalDate) || !(value[1] instanceof LocalDate)) { + throw new IllegalArgumentException("Illegal method signature, expected two parameters of type LocalDate."); + } + + return ((LocalDate) value[0]).isBefore((LocalDate) value[1]); + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java new file mode 100644 index 0000000000..6b321f545c --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java @@ -0,0 +1,23 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Constraint(validatedBy = ConsistentDateParameterValidator.class) +@Target({ METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Documented +public @interface ConsistentDateParameters { + + String message() default "End date must be after begin date and both must be in the future"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java new file mode 100644 index 0000000000..f9cdea1483 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java @@ -0,0 +1,24 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Constraint(validatedBy = ValidReservationValidator.class) +@Target({ METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Documented +public @interface ValidReservation { + + String message() default "End date must be after begin date and both must be in the future, room number must be bigger than 0"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java new file mode 100644 index 0000000000..1da257044b --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java @@ -0,0 +1,40 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import org.baeldung.javaxval.methodvalidation.model.Reservation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.time.LocalDate; + +public class ValidReservationValidator implements ConstraintValidator { + + @Override + public void initialize(ValidReservation constraintAnnotation) { + } + + @Override + public boolean isValid(Reservation reservation, ConstraintValidatorContext context) { + + if (reservation == null) { + return true; + } + + if (!(reservation instanceof Reservation)) { + throw new IllegalArgumentException("Illegal method signature, expected parameter of type Reservation."); + } + + if (reservation.getBegin() == null || reservation.getEnd() == null || reservation.getCustomer() == null) { + return false; + } + + if (reservation.getBegin() + .isAfter(LocalDate.now()) + && reservation.getBegin() + .isBefore(reservation.getEnd()) + && reservation.getRoom() > 0) { + + return true; + } + return false; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java new file mode 100644 index 0000000000..529cf436da --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java @@ -0,0 +1,41 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Validated +public class Customer { + + @Size(min = 5, max = 200) + private String firstName; + + @Size(min = 5, max = 200) + private String lastName; + + public Customer(@Size(min = 5, max = 200) @NotNull String firstName, @Size(min = 5, max = 200) String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public Customer() { + + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java new file mode 100644 index 0000000000..89f13e9e37 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java @@ -0,0 +1,60 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.baeldung.javaxval.methodvalidation.constraints.ConsistentDateParameters; +import org.baeldung.javaxval.methodvalidation.constraints.ValidReservation; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDate; + +@Validated +public class Reservation { + + private LocalDate begin; + + private LocalDate end; + + private Customer customer; + + private int room; + + @ConsistentDateParameters + @ValidReservation + public Reservation(LocalDate begin, LocalDate end, Customer customer, int room) { + this.begin = begin; + this.end = end; + this.customer = customer; + this.room = room; + } + + public LocalDate getBegin() { + return begin; + } + + public void setBegin(LocalDate begin) { + this.begin = begin; + } + + public LocalDate getEnd() { + return end; + } + + public void setEnd(LocalDate end) { + this.end = end; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public int getRoom() { + return room; + } + + public void setRoom(int room) { + this.room = room; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java new file mode 100644 index 0000000000..0aeeb52e1f --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java @@ -0,0 +1,50 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.baeldung.javaxval.methodvalidation.constraints.ConsistentDateParameters; +import org.baeldung.javaxval.methodvalidation.constraints.ValidReservation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.*; +import java.time.LocalDate; +import java.util.List; + +@Controller +@Validated +public class ReservationManagement { + + @Autowired + private ApplicationContext applicationContext; + + @ConsistentDateParameters + public void createReservation(LocalDate begin, LocalDate end, @NotNull Customer customer) { + + // ... + } + + public void createReservation(@NotNull @Future LocalDate begin, @Min(1) int duration, @NotNull Customer customer) { + + // ... + } + + @NotNull + @Size(min = 1) + public List<@NotNull Customer> getAllCustomers() { + + return null; + } + + public void createNewCustomer(@Valid Customer customer) { + + // ... + } + + @Valid + public Customer getCustomerById() { + + return null; + } +} diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java new file mode 100644 index 0000000000..53133edd48 --- /dev/null +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java @@ -0,0 +1,86 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.ReservationManagement; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import javax.validation.ConstraintViolationException; +import java.time.LocalDate; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { MethodValidationConfig.class }, loader = AnnotationConfigContextLoader.class) +public class ContainerValidationIntegrationTest { + + @Autowired + ReservationManagement reservationManagement; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void whenValidationWithInvalidMethodParameters_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + reservationManagement.createReservation(LocalDate.now(), 0, null); + } + + @Test + public void whenValidationWithValidMethodParameters_thenNoException() { + + reservationManagement.createReservation(LocalDate.now() + .plusDays(1), 1, new Customer("William", "Smith")); + } + + @Test + public void whenCrossParameterValidationWithInvalidParameters_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + reservationManagement.createReservation(LocalDate.now(), LocalDate.now(), null); + } + + @Test + public void whenCrossParameterValidationWithValidParameters_thenNoException() { + + reservationManagement.createReservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith")); + } + + @Test + public void whenValidationWithInvalidReturnValue_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + List list = reservationManagement.getAllCustomers(); + } + + @Test + public void whenValidationWithInvalidCascadedValue_thenConstraintViolationException() { + + Customer customer = new Customer(); + customer.setFirstName("John"); + customer.setLastName("Doe"); + + exception.expect(ConstraintViolationException.class); + reservationManagement.createNewCustomer(customer); + } + + @Test + public void whenValidationWithValidCascadedValue_thenCNoException() { + + Customer customer = new Customer(); + customer.setFirstName("William"); + customer.setLastName("Smith"); + + reservationManagement.createNewCustomer(customer); + } +} diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java new file mode 100644 index 0000000000..6a750698c6 --- /dev/null +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java @@ -0,0 +1,199 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; +import org.baeldung.javaxval.methodvalidation.model.ReservationManagement; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.executable.ExecutableValidator; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.time.LocalDate; +import java.util.Collections; +import java.util.Set; + +public class ValidationIntegrationTest { + + private ExecutableValidator executableValidator; + + @Before + public void getExecutableValidator() { + + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + this.executableValidator = factory.getValidator() + .forExecutables(); + } + + @Test + public void whenValidationWithInvalidMethodParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, int.class, Customer.class); + Object[] parameterValues = { LocalDate.now(), 0, null }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(3, violations.size()); + } + + @Test + public void whenValidationWithValidMethodParameters_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, int.class, Customer.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), 1, new Customer("John", "Doe") }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithInvalidParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, LocalDate.class, Customer.class); + Object[] parameterValues = { LocalDate.now(), LocalDate.now(), new Customer("John", "Doe") }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(1, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithValidParameters_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, LocalDate.class, Customer.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("John", "Doe") }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidConstructorParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Customer.class.getConstructor(String.class, String.class); + Object[] parameterValues = { "John", "Doe" }; + Set> violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(2, violations.size()); + } + + @Test + public void whenValidationWithValidConstructorParameters_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Customer.class.getConstructor(String.class, String.class); + Object[] parameterValues = { "William", "Smith" }; + Set> violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithInvalidConstructorParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Object[] parameterValues = { LocalDate.now(), LocalDate.now(), new Customer("William", "Smith"), 1 }; + Set> violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(1, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithValidConstructorParameters_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith"), 1 }; + Set> violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidReturnValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("getAllCustomers"); + Object returnValue = Collections. emptyList(); + Set> violations = executableValidator.validateReturnValue(object, method, returnValue); + + assertEquals(1, violations.size()); + } + + @Test + public void whenValidationWithValidReturnValue_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("getAllCustomers"); + Object returnValue = Collections.singletonList(new Customer("William", "Smith")); + Set> violations = executableValidator.validateReturnValue(object, method, returnValue); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidConstructorReturnValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Reservation createdObject = new Reservation(LocalDate.now(), LocalDate.now(), new Customer("William", "Smith"), 0); + Set> violations = executableValidator.validateConstructorReturnValue(constructor, createdObject); + + assertEquals(1, violations.size()); + } + + @Test + public void whenValidationWithValidConstructorReturnValue_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Reservation createdObject = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith"), 1); + Set> violations = executableValidator.validateConstructorReturnValue(constructor, createdObject); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createNewCustomer", Customer.class); + Customer customer = new Customer(); + customer.setFirstName("John"); + customer.setLastName("Doe"); + Object[] parameterValues = { customer }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(2, violations.size()); + } + + @Test + public void whenValidationWithValidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createNewCustomer", Customer.class); + Customer customer = new Customer(); + customer.setFirstName("William"); + customer.setLastName("Smith"); + Object[] parameterValues = { customer }; + Set> violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + +} From 999dfbf381c621c2b484d309534d5e279da87e0e Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 27 Jan 2018 17:43:23 +0200 Subject: [PATCH 067/118] remove enablemvc --- .../internationalization/config/MvcConfig.java | 4 +--- .../src/main/resources/static/internationalization.js | 8 ++++++++ .../src/main/resources/templates/international.html | 11 +---------- 3 files changed, 10 insertions(+), 13 deletions(-) create mode 100644 spring-boot/src/main/resources/static/internationalization.js 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/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 - +

From f9649c0926da4925fb6849a1f45aadc58bfcf6d7 Mon Sep 17 00:00:00 2001 From: Dassi orleando Date: Sat, 27 Jan 2018 22:52:46 +0100 Subject: [PATCH 068/118] BAEL-1273: move code to another module (#3532) * BAEL-1216: improve tests * BAEL-1448: Update Spring 5 articles to use the release version * Setting up the Maven Wrapper on a maven project * Add Maven Wrapper on spring-boot module * simple add * BAEL-976: Update spring version * BAEL-1273: Display RSS feed with spring mvc (AbstractRssFeedView) * Move RSS feed with Spring MVC from spring-boot to spring-mvc-simple --- spring-mvc-simple/pom.xml | 6 +++ .../controller/rss/ArticleFeedView.java | 54 +++++++++++++++++++ .../controller/rss/ArticleRssController.java | 14 +++++ 3 files changed, 74 insertions(+) create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java diff --git a/spring-mvc-simple/pom.xml b/spring-mvc-simple/pom.xml index 05b2eb49b6..595e58f5f3 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -30,6 +30,7 @@ 5.0.2 5.0.2 1.0.2 + 1.9.0
@@ -115,6 +116,11 @@ ${junit.jupiter.version} test + + com.rometools + rome + ${rome.version} + 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"; + } + +} From 0d85d1ad01015f719a7f7b5f65ab731925af4886 Mon Sep 17 00:00:00 2001 From: Adam InTae Gerard Date: Sun, 28 Jan 2018 05:11:10 -0800 Subject: [PATCH 069/118] BAEL-1175 - corrected directory cmd (#3534) --- spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh | 2 +- spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh index 577a25dd6e..f1c6bfcf3f 100644 --- a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -6,6 +6,6 @@ git clone https://github.com/spring-cloud-stream-app-starters/hdfs.git ./mvnw clean install -PgenerateApps # Run it -cd apps +cd target # 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/twitter.sh b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh index 4c76fe637b..967cb54dfe 100644 --- a/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -6,7 +6,7 @@ git clone https://github.com/spring-cloud-stream-app-starters/twitter.git ./mvnw clean install -PgenerateApps # Run it -cd apps +cd target # Optionally inject application.properties prior to build java -jar twitter_stream_source.jar --consumerKey= --consumerSecret= \ --accessToken= --accessTokenSecret= \ No newline at end of file From 4d8f0a48ae285bd681758f5da5ab785fa91b6fa1 Mon Sep 17 00:00:00 2001 From: k0l0ssus Date: Sun, 28 Jan 2018 18:06:11 -0500 Subject: [PATCH 070/118] Add Java vs Vavr Stream sample --- .../samples/java/vavr/VavrSampler.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java diff --git a/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java new file mode 100644 index 0000000000..ae06c97e2e --- /dev/null +++ b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java @@ -0,0 +1,99 @@ +package com.baeldung.samples.java.vavr; + +import io.vavr.collection.Stream; +import java.util.ArrayList; +import java.util.List; + + +/** + * + * @author baeldung + */ +public class VavrSampler { + + static int[] intArray = new int[]{1, 2, 4}; + static List intList = new ArrayList(); + static int[][] intOfInts = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + + public static void main(String[] args) { + vavrStreamElementAccess(); + System.out.println("===================================="); + vavrParallelStreamAccess(); + System.out.println("===================================="); + vavrFlatMapping(); + System.out.println("===================================="); + vavrStreamManipulation(); + System.out.println("===================================="); + vavrStreamDistinct(); + } + + public static void vavrStreamElementAccess() { + System.out.println("Vavr Element Access"); + System.out.println("===================================="); + Stream vavredStream = Stream.ofAll(intArray); + System.out.println("Vavr index access: " + vavredStream.get(2)); + System.out.println("Vavr head element access: " + vavredStream.get()); + + Stream vavredStringStream = Stream.of("foo", "bar", "baz"); + System.out.println("Find foo " + vavredStringStream.indexOf("foo")); + } + + public static void vavrParallelStreamAccess() { + + System.out.println("Vavr Stream Concurrent Modification"); + System.out.println("===================================="); + Stream vavrStream = Stream.ofAll(intList); + //intList.add(5); + vavrStream.forEach(i -> System.out.println("in a Vavr Stream: " + i)); + +// Stream wrapped = Stream.ofAll(intArray); +// intArray[2] = 5; +// wrapped.forEach(i -> System.out.println("Vavr looped " + i)); + } + + public static void jdkFlatMapping() { + System.out.println("Java flatMapping"); + System.out.println("===================================="); + java.util.stream.Stream.of(42).flatMap(i -> java.util.stream.Stream.generate(() -> { + System.out.println("nested call"); + return 42; + })).findAny(); + } + + public static void vavrFlatMapping() { + System.out.println("Vavr flatMapping"); + System.out.println("===================================="); + Stream.of(42) + .flatMap(i -> Stream.continually(() -> { + System.out.println("nested call"); + return 42; + })) + .get(0); + } + + public static void vavrStreamManipulation() { + System.out.println("Vavr Stream Manipulation"); + System.out.println("===================================="); + List stringList = new ArrayList<>(); + stringList.add("foo"); + stringList.add("bar"); + stringList.add("baz"); + Stream vavredStream = Stream.ofAll(stringList); + vavredStream.forEach(item -> System.out.println("Vavr Stream item: " + item)); + Stream vavredStream2 = vavredStream.insert(2, "buzz"); + vavredStream2.forEach(item -> System.out.println("Vavr Stream item after stream addition: " + item)); + stringList.forEach(item -> System.out.println("List item after stream addition: " + item)); + Stream deletionStream = vavredStream.remove("bar"); + deletionStream.forEach(item -> System.out.println("Vavr Stream item after stream item deletion: " + item)); + + } + + public static void vavrStreamDistinct() { + Stream vavredStream = Stream.of("foo", "bar", "baz", "buxx", "bar", "bar", "foo"); + Stream distinctVavrStream = vavredStream.distinctBy((y, z) -> { + return y.compareTo(z); + }); + distinctVavrStream.forEach(item -> System.out.println("Vavr Stream item after distinct query " + item)); + + } +} From f888a3f78accaa6588f36528b8eee5a8c27772bc Mon Sep 17 00:00:00 2001 From: Bogdan Stoean <4540392+BogdanStoean@users.noreply.github.com> Date: Mon, 29 Jan 2018 07:44:40 +0200 Subject: [PATCH 071/118] BAEL-1410 - refactor tests (#3525) * initial setup with spring boot/ spring data jpa/ flyway * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - created multiple migration scripts and locations * BAEL-1315 - test insert after schema creation * cleanup * BAEL-1315 - test data changes by a migration * [BAEL-1410] Spring Boot Security Auto-Configuration * [BAEL-1410] Added some tests for incorrect credentials use case * [BAEL-1410] Added readme and some code improvements * [BAEL-1410] removed form based auth config because is redundant added oauth2 server auto-configuration sample with test * [BAEL-1410] added custom Authorization Server Config * [BAEL-1410] update README * [BAEL-1410]refactor tests * [BAEL-1410]oauth2 resource server * [BAEL-1410]oauth2 sso sample with facebook * [BAEL-1410]remove spring-flyway * [BAEL-1410]refactor tests * [BAEL-1410] refactor tests * [BAEL-1410] update --- ...BasicAuthConfigurationIntegrationTest.java | 2 + ...figAuthorizationServerIntegrationTest.java | 55 ++++++------------- ...figAuthorizationServerIntegrationTest.java | 26 +++------ .../OAuth2IntegrationTestSupport.java | 34 ++++++++++++ 4 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java index 4e4244abb7..32c3fbdef4 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java @@ -37,6 +37,7 @@ public class BasicAuthConfigurationIntegrationTest { @Test public void whenLoggedUserRequestsHomePage_ThenSuccess() throws IllegalStateException, IOException { ResponseEntity response = restTemplate.getForEntity(base.toString(), String.class); + assertEquals(HttpStatus.OK, response.getStatusCode()); assertTrue(response .getBody() @@ -47,6 +48,7 @@ public class BasicAuthConfigurationIntegrationTest { public void whenUserWithWrongCredentialsRequestsHomePage_ThenUnauthorizedPage() throws IllegalStateException, IOException { restTemplate = new TestRestTemplate("user", "wrongpassword"); ResponseEntity response = restTemplate.getForEntity(base.toString(), String.class); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); assertTrue(response .getBody() diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java index 09df9ce645..cc953eef5a 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java @@ -2,10 +2,7 @@ package com.baeldung.springbootsecurity.oauth2server; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; @@ -13,7 +10,6 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import static java.lang.String.format; import static java.util.Collections.singletonList; import static org.junit.Assert.assertNotNull; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -21,54 +17,35 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class) @ActiveProfiles("authz") -public class CustomConfigAuthorizationServerIntegrationTest { - - @Value("${local.server.port}") protected int port; +public class CustomConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport { @Test - public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("read")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("read")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); } @Test(expected = OAuth2AccessDeniedException.class) - public void whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + restTemplate.getAccessToken(); } @Test - public void whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung-admin"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); - OAuth2AccessToken accessToken = restTemplate.getAccessToken(); - assertNotNull(accessToken); - } + public void givenOAuth2Context_whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung-admin", singletonList("write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); - private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() { - ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); - resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); - resourceDetails.setGrantType("client_credentials"); - return resourceDetails; + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + + assertNotNull(accessToken); } } diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java index c7b1b4ef6c..4d7b449380 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java @@ -2,40 +2,28 @@ package com.baeldung.springbootsecurity.oauth2server; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.test.context.junit4.SpringRunner; -import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.junit.Assert.assertNotNull; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class, - properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=secret" }) -public class DefaultConfigAuthorizationServerIntegrationTest { - - @Value("${local.server.port}") protected int port; + properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=baeldung" }) +public class DefaultConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport { @Test - public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); - resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); - resourceDetails.setClientId("client"); - resourceDetails.setClientSecret("secret"); - resourceDetails.setGrantType("client_credentials"); - resourceDetails.setScope(asList("read", "write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("client", asList("read", "write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); } diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java new file mode 100644 index 0000000000..3eef206c7d --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java @@ -0,0 +1,34 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; + +import java.util.List; + +import static java.lang.String.format; +import static java.util.Collections.singletonList; + +public class OAuth2IntegrationTestSupport { + + @Value("${local.server.port}") protected int port; + + protected ClientCredentialsResourceDetails getClientCredentialsResourceDetails(final String clientId, final List scopes) { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setClientId(clientId); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(scopes); + resourceDetails.setGrantType("client_credentials"); + return resourceDetails; + } + + protected OAuth2RestTemplate getOAuth2RestTemplate(final ClientCredentialsResourceDetails resourceDetails) { + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + return restTemplate; + } +} From b070f3ddaa207a66101a5ea1e51ad4e52854ae97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carsten=20Gr=C3=A4f?= Date: Mon, 29 Jan 2018 07:37:17 +0100 Subject: [PATCH 072/118] fixed formatting --- .../samples/java/vavr/VavrSampler.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java index ae06c97e2e..f4e0728f32 100644 --- a/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java +++ b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java @@ -1,9 +1,9 @@ package com.baeldung.samples.java.vavr; -import io.vavr.collection.Stream; import java.util.ArrayList; import java.util.List; +import io.vavr.collection.Stream; /** * @@ -11,12 +11,12 @@ import java.util.List; */ public class VavrSampler { - static int[] intArray = new int[]{1, 2, 4}; - static List intList = new ArrayList(); - static int[][] intOfInts = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + static int[] intArray = new int[] { 1, 2, 4 }; + static List intList = new ArrayList<>(); + static int[][] intOfInts = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; public static void main(String[] args) { - vavrStreamElementAccess(); + vavrStreamElementAccess(); System.out.println("===================================="); vavrParallelStreamAccess(); System.out.println("===================================="); @@ -43,12 +43,12 @@ public class VavrSampler { System.out.println("Vavr Stream Concurrent Modification"); System.out.println("===================================="); Stream vavrStream = Stream.ofAll(intList); - //intList.add(5); + // intList.add(5); vavrStream.forEach(i -> System.out.println("in a Vavr Stream: " + i)); -// Stream wrapped = Stream.ofAll(intArray); -// intArray[2] = 5; -// wrapped.forEach(i -> System.out.println("Vavr looped " + i)); + // Stream wrapped = Stream.ofAll(intArray); + // intArray[2] = 5; + // wrapped.forEach(i -> System.out.println("Vavr looped " + i)); } public static void jdkFlatMapping() { @@ -65,9 +65,9 @@ public class VavrSampler { System.out.println("===================================="); Stream.of(42) .flatMap(i -> Stream.continually(() -> { - System.out.println("nested call"); - return 42; - })) + System.out.println("nested call"); + return 42; + })) .get(0); } From d1a4848cb49bbf4ec6fe720fea109f715be53779 Mon Sep 17 00:00:00 2001 From: Aprian Diaz Novandi Date: Mon, 29 Jan 2018 09:26:15 +0100 Subject: [PATCH 073/118] BAEL-1464 Guava Memoizer (#3420) * BAEL-1464 Guava Memoizer * Update codes based on code review and discussion in JIRA --- .../guava/memoizer/CostlySupplier.java | 17 ++++ .../baeldung/guava/memoizer/Factorial.java | 22 +++++ .../guava/memoizer/FibonacciSequence.java | 26 +++++ .../baeldung/guava/GuavaMemoizerUnitTest.java | 98 +++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java create mode 100644 guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java create mode 100644 guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java create mode 100644 guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java b/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java new file mode 100644 index 0000000000..63b3fbd438 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java @@ -0,0 +1,17 @@ +package org.baeldung.guava.memoizer; + +import java.math.BigInteger; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class CostlySupplier { + + public static BigInteger generateBigNumber() { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + } + return new BigInteger("12345"); + } + +} diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java b/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java new file mode 100644 index 0000000000..74fcbdcc14 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java @@ -0,0 +1,22 @@ +package org.baeldung.guava.memoizer; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.math.BigInteger; + +public class Factorial { + + private static LoadingCache memo = CacheBuilder.newBuilder() + .build(CacheLoader.from(Factorial::getFactorial)); + + public static BigInteger getFactorial(int n) { + if (n == 0) { + return BigInteger.ONE; + } else { + return BigInteger.valueOf(n).multiply(memo.getUnchecked(n - 1)); + } + } + +} diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java b/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java new file mode 100644 index 0000000000..0c70f08c23 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java @@ -0,0 +1,26 @@ +package org.baeldung.guava.memoizer; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +public class FibonacciSequence { + + private static LoadingCache memo = CacheBuilder.newBuilder() + .maximumSize(100) + .build(CacheLoader.from(FibonacciSequence::getFibonacciNumber)); + + public static BigInteger getFibonacciNumber(int n) { + if (n == 0) { + return BigInteger.ZERO; + } else if (n == 1) { + return BigInteger.ONE; + } else { + return memo.getUnchecked(n - 1).add(memo.getUnchecked(n - 2)); + } + } + +} diff --git a/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java new file mode 100644 index 0000000000..0ae1f438e3 --- /dev/null +++ b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java @@ -0,0 +1,98 @@ +package org.baeldung.guava; + +import com.google.common.base.Suppliers; +import org.baeldung.guava.memoizer.CostlySupplier; +import org.baeldung.guava.memoizer.Factorial; +import org.baeldung.guava.memoizer.FibonacciSequence; +import org.junit.Test; + +import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.number.IsCloseTo.closeTo; +import static org.junit.Assert.assertThat; + +public class GuavaMemoizerUnitTest { + + @Test + public void givenInteger_whenGetFibonacciNumber_thenShouldCalculateFibonacciNumber() { + // given + int n = 95; + + // when + BigInteger fibonacciNumber = FibonacciSequence.getFibonacciNumber(n); + + // then + BigInteger expectedFibonacciNumber = new BigInteger("31940434634990099905"); + assertThat(fibonacciNumber, is(equalTo(expectedFibonacciNumber))); + } + + @Test + public void givenInteger_whenGetFactorial_thenShouldCalculateFactorial() { + // given + int n = 95; + + // when + BigInteger factorial = new Factorial().getFactorial(n); + + // then + BigInteger expectedFactorial = new BigInteger("10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000"); + assertThat(factorial, is(equalTo(expectedFactorial))); + } + + @Test + public void givenMemoizedSupplier_whenGet_thenSubsequentGetsAreFast() { + Supplier memoizedSupplier; + memoizedSupplier = Suppliers.memoize(CostlySupplier::generateBigNumber); + + Instant start = Instant.now(); + BigInteger bigNumber = memoizedSupplier.get(); + Long durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12345")))); + assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); + + start = Instant.now(); + bigNumber = memoizedSupplier.get().add(BigInteger.ONE); + durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12346")))); + assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); + + start = Instant.now(); + bigNumber = memoizedSupplier.get().add(BigInteger.TEN); + durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12355")))); + assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); + } + + @Test + public void givenMemoizedSupplierWithExpiration_whenGet_thenSubsequentGetsBeforeExpiredAreFast() throws InterruptedException { + Supplier memoizedSupplier; + memoizedSupplier = Suppliers.memoizeWithExpiration(CostlySupplier::generateBigNumber, 3, TimeUnit.SECONDS); + + Instant start = Instant.now(); + BigInteger bigNumber = memoizedSupplier.get(); + Long durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12345")))); + assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); + + start = Instant.now(); + bigNumber = memoizedSupplier.get().add(BigInteger.ONE); + durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12346")))); + assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); + + TimeUnit.SECONDS.sleep(1); + + start = Instant.now(); + bigNumber = memoizedSupplier.get().add(BigInteger.TEN); + durationInMs = Duration.between(start, Instant.now()).toMillis(); + assertThat(bigNumber, is(equalTo(new BigInteger("12355")))); + assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); + } + +} From be90e9870c12864b651bc2093a8cfea01563805f Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Mon, 29 Jan 2018 17:02:48 +0700 Subject: [PATCH 074/118] Initial commit for BAEL-1500 --- .../main/java/com/baeldung/javac/Data.java | 28 +++++++++++++++++++ core-java/src/main/java/javac-args/arguments | 2 ++ core-java/src/main/java/javac-args/options | 2 ++ core-java/src/main/java/javac-args/types | 1 + core-java/src/main/java/javac-args/xlint-ops | 3 ++ 5 files changed, 36 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/javac/Data.java create mode 100644 core-java/src/main/java/javac-args/arguments create mode 100644 core-java/src/main/java/javac-args/options create mode 100644 core-java/src/main/java/javac-args/types create mode 100644 core-java/src/main/java/javac-args/xlint-ops diff --git a/core-java/src/main/java/com/baeldung/javac/Data.java b/core-java/src/main/java/com/baeldung/javac/Data.java new file mode 100644 index 0000000000..f6912fbd14 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/javac/Data.java @@ -0,0 +1,28 @@ +package com.baeldung.javac; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class Data implements Serializable { + static List textList = new ArrayList(); + + private static void addText() { + textList.add("baeldung"); + textList.add("."); + textList.add("com"); + } + + public List getTextList() { + this.addText(); + List result = new ArrayList(); + String firstElement = (String) textList.get(0); + switch (firstElement) { + case "baeldung": + result.add("baeldung"); + case "com": + result.add("com"); + } + return result; + } +} diff --git a/core-java/src/main/java/javac-args/arguments b/core-java/src/main/java/javac-args/arguments new file mode 100644 index 0000000000..51639800a7 --- /dev/null +++ b/core-java/src/main/java/javac-args/arguments @@ -0,0 +1,2 @@ +-d javac-target -verbose +com/baeldung/javac/Data.java \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/options b/core-java/src/main/java/javac-args/options new file mode 100644 index 0000000000..f02f2344ff --- /dev/null +++ b/core-java/src/main/java/javac-args/options @@ -0,0 +1,2 @@ +-d javac-target +-verbose \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/types b/core-java/src/main/java/javac-args/types new file mode 100644 index 0000000000..ef2d861f84 --- /dev/null +++ b/core-java/src/main/java/javac-args/types @@ -0,0 +1 @@ +com/baeldung/javac/Data.java \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/xlint-ops b/core-java/src/main/java/javac-args/xlint-ops new file mode 100644 index 0000000000..cdccbc0cce --- /dev/null +++ b/core-java/src/main/java/javac-args/xlint-ops @@ -0,0 +1,3 @@ +-d javac-target +-Xlint:rawtypes,unchecked,static,cast,serial,fallthrough +com/baeldung/javac/Data.java \ No newline at end of file From bdae4fe99bd201f37a187e08f93fa8ca063ddd3a Mon Sep 17 00:00:00 2001 From: abialas Date: Tue, 30 Jan 2018 03:32:09 +0100 Subject: [PATCH 075/118] BAEL-21 new Java9 HTTP API overview (#3536) * BAEL-1412 add java 8 spring data features * BAEL-21 new HTTP API overview --- .../java9/httpclient/HttpClientTest.java | 215 ++++++++++++++++++ .../java9/httpclient/HttpRequestTest.java | 171 ++++++++++++++ .../java9/httpclient/HttpResponseTest.java | 55 +++++ 3 files changed, 441 insertions(+) create mode 100644 core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java create mode 100644 core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java create mode 100644 core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java new file mode 100644 index 0000000000..a4c6ac0d7d --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java @@ -0,0 +1,215 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.IOException; +import java.net.*; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpClientTest { + + @Test + public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample body")) + .build(); + + HttpResponse response = HttpClient.newBuilder() + .proxy(ProxySelector.getDefault()) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample body")); + } + + @Test + public void shouldNotFollowRedirectWhenSetToDefaultNever() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM)); + assertThat(response.body(), containsString("https://stackoverflow.com/")); + } + + @Test + public void shouldFollowRedirectWhenSetToAlways() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.finalRequest() + .uri() + .toString(), equalTo("https://stackoverflow.com/")); + } + + @Test + public void shouldReturnOKStatusForAuthenticatedAccess() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/basic-auth")) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("postman", "password".toCharArray()); + } + }) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldSendRequestAsync() throws URISyntaxException, InterruptedException, ExecutionException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample body")) + .build(); + CompletableFuture> response = HttpClient.newBuilder() + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldUseJustTwoThreadWhenProcessingSendAsyncRequest() throws URISyntaxException, InterruptedException, ExecutionException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + CompletableFuture> response1 = HttpClient.newBuilder() + .executor(Executors.newFixedThreadPool(2)) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture> response2 = HttpClient.newBuilder() + .executor(Executors.newFixedThreadPool(2)) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture> response3 = HttpClient.newBuilder() + .executor(Executors.newFixedThreadPool(2)) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture.allOf(response1, response2, response3) + .join(); + + assertThat(response1.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response2.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response3.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldNotStoreCookieWhenPolicyAcceptNone() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpClient httpClient = HttpClient.newBuilder() + .cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_NONE)) + .build(); + + httpClient.send(request, HttpResponse.BodyHandler.asString()); + + assertThat(httpClient.cookieManager() + .get() + .getCookieStore() + .getCookies(), empty()); + } + + @Test + public void shouldStoreCookieWhenPolicyAcceptAll() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpClient httpClient = HttpClient.newBuilder() + .cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_ALL)) + .build(); + + httpClient.send(request, HttpResponse.BodyHandler.asString()); + + assertThat(httpClient.cookieManager() + .get() + .getCookieStore() + .getCookies(), not(empty())); + } + + @Test + public void shouldProcessMultipleRequestViaStream() throws URISyntaxException, ExecutionException, InterruptedException { + List targets = Arrays.asList(new URI("https://postman-echo.com/get?foo1=bar1"), new URI("https://postman-echo.com/get?foo2=bar2")); + + HttpClient client = HttpClient.newHttpClient(); + + List> futures = targets.stream() + .map(target -> client.sendAsync(HttpRequest.newBuilder(target) + .GET() + .build(), HttpResponse.BodyHandler.asString()) + .thenApply(response -> response.body())) + .collect(Collectors.toList()); + + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .join(); + + if (futures.get(0) + .get() + .contains("foo1")) { + assertThat(futures.get(0) + .get(), containsString("bar1")); + assertThat(futures.get(1) + .get(), containsString("bar2")); + } else { + assertThat(futures.get(1) + .get(), containsString("bar2")); + assertThat(futures.get(1) + .get(), containsString("bar1")); + } + + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java new file mode 100644 index 0000000000..7c0e9a90e0 --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java @@ -0,0 +1,171 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; + +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpRequestTest { + + @Test + public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldUseHttp2WhenWebsiteUsesHttp2() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://stackoverflow.com")) + .version(HttpClient.Version.HTTP_2) + .GET() + .build(); + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.version(), equalTo(HttpClient.Version.HTTP_2)); + } + + @Test + public void shouldFallbackToHttp1_1WhenWebsiteDoesNotUseHttp2() throws IOException, InterruptedException, URISyntaxException, NoSuchAlgorithmException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .version(HttpClient.Version.HTTP_2) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1)); + } + + @Test + public void shouldReturnStatusOKWhenSendGetRequestWithDummyHeaders() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .headers("key1", "value1", "key2", "value2") + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnStatusOKWhenSendGetRequestTimeoutSet() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .timeout(Duration.of(10, SECONDS)) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnNoContentWhenPostWithNoBody() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .POST(HttpRequest.noBody()) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithBodyText() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample request body")) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithInputStream() throws IOException, InterruptedException, URISyntaxException { + byte[] sampleData = "Sample request body".getBytes(); + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromInputStream(() -> new ByteArrayInputStream(sampleData))) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithByteArrayProcessorStream() throws IOException, InterruptedException, URISyntaxException { + byte[] sampleData = "Sample request body".getBytes(); + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromByteArray(sampleData)) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithFileProcessorStream() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromFile(Paths.get("src/test/resources/sample.txt"))) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample file content")); + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java new file mode 100644 index 0000000000..80295ff34c --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java @@ -0,0 +1,55 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpResponseTest { + + @Test + public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), not(isEmptyString())); + } + + @Test + public void shouldResponseURIDifferentThanRequestUIRWhenRedirect() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(request.uri() + .toString(), equalTo("http://stackoverflow.com")); + assertThat(response.uri() + .toString(), equalTo("https://stackoverflow.com/")); + } + +} From 32e309ae4d62f3b38334ad60977660cb55c1deff Mon Sep 17 00:00:00 2001 From: Ganesh Date: Tue, 30 Jan 2018 20:25:08 +0530 Subject: [PATCH 076/118] BAEL-1456 - How to implement task prioritization in Java (#3366) * priority based job execution in java * minor fixes * updated to use java 8 features --- .../concurrent/prioritytaskexecution/Job.java | 24 ++++++++ .../prioritytaskexecution/JobPriority.java | 7 +++ .../PriorityJobScheduler.java | 56 +++++++++++++++++++ .../PriorityJobSchedulerUnitTest.java | 38 +++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java create mode 100644 core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java create mode 100644 core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java create mode 100644 core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java new file mode 100644 index 0000000000..a70041ed7d --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java @@ -0,0 +1,24 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +public class Job implements Runnable { + private String jobName; + private JobPriority jobPriority; + + public Job(String jobName, JobPriority jobPriority) { + this.jobName = jobName; + this.jobPriority = jobPriority != null ? jobPriority : JobPriority.MEDIUM; + } + + public JobPriority getJobPriority() { + return jobPriority; + } + + @Override + public void run() { + try { + System.out.println("Job:" + jobName + " Priority:" + jobPriority); + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } + } +} \ No newline at end of file diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java new file mode 100644 index 0000000000..d8092a9ce7 --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java @@ -0,0 +1,7 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +public enum JobPriority { + HIGH, + MEDIUM, + LOW +} diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java new file mode 100644 index 0000000000..70fd1710c0 --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java @@ -0,0 +1,56 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +import java.util.Comparator; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class PriorityJobScheduler { + + private ExecutorService priorityJobPoolExecutor; + private ExecutorService priorityJobScheduler; + private PriorityBlockingQueue priorityQueue; + + public PriorityJobScheduler(Integer poolSize, Integer queueSize) { + priorityJobPoolExecutor = Executors.newFixedThreadPool(poolSize); + Comparator jobComparator = Comparator.comparing(Job::getJobPriority); + priorityQueue = new PriorityBlockingQueue(queueSize, + (Comparator) jobComparator); + + priorityJobScheduler = Executors.newSingleThreadExecutor(); + priorityJobScheduler.execute(()->{ + while (true) { + try { + priorityJobPoolExecutor.execute(priorityQueue.take()); + } catch (InterruptedException e) { + break; + } + } + }); + } + + public void scheduleJob(Job job) { + priorityQueue.add(job); + } + + public int getQueuedTaskCount() { + return priorityQueue.size(); + } + + protected void close(ExecutorService scheduler) { + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + } + } + + public void closeScheduler() { + close(priorityJobPoolExecutor); + close(priorityJobScheduler); + } +} diff --git a/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java b/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java new file mode 100644 index 0000000000..902bada3a2 --- /dev/null +++ b/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +import org.junit.Test; + +public class PriorityJobSchedulerUnitTest { + private static int POOL_SIZE = 1; + private static int QUEUE_SIZE = 10; + + @Test + public void whenMultiplePriorityJobsQueued_thenHighestPriorityJobIsPicked() { + Job job1 = new Job("Job1", JobPriority.LOW); + Job job2 = new Job("Job2", JobPriority.MEDIUM); + Job job3 = new Job("Job3", JobPriority.HIGH); + Job job4 = new Job("Job4", JobPriority.MEDIUM); + Job job5 = new Job("Job5", JobPriority.LOW); + Job job6 = new Job("Job6", JobPriority.HIGH); + + PriorityJobScheduler pjs = new PriorityJobScheduler(POOL_SIZE, QUEUE_SIZE); + + pjs.scheduleJob(job1); + pjs.scheduleJob(job2); + pjs.scheduleJob(job3); + pjs.scheduleJob(job4); + pjs.scheduleJob(job5); + pjs.scheduleJob(job6); + + // ensure no tasks is pending before closing the scheduler + while (pjs.getQueuedTaskCount() != 0); + + // delay to avoid job sleep (added for demo) being interrupted + try { + Thread.sleep(2000); + } catch (InterruptedException ignored) { + } + + pjs.closeScheduler(); + } +} From f6cfff3f9d2be2018af280b2647bb3a01081d715 Mon Sep 17 00:00:00 2001 From: iaforek Date: Tue, 30 Jan 2018 18:13:53 +0000 Subject: [PATCH 077/118] BAEL-1298 - How to create a Sudoku solver (#3197) * Code for Dependency Injection Article. * Added Java based configuration. Downloaded formatter.xml and reformatted all changed files. Manually changed tab into 4 spaces in XML configuration files. * BAEL-434 - Spring Roo project files generated by Spring Roo. No formatting applied. Added POM, java and resources folders. * Moved project from roo to spring-roo folder. * BAEL-838 Initial code showing how to remove last char - helper class and tests. * BAEL-838 Corrected Helper class and associated empty string test case. Added StringUtils.substing tests. * BAEL-838 Refromatted code using formatter.xml. Added Assert.assertEquals import. Renamed test to follow convention. Reordered tests. * BAEL-838 - Added regex method and updated tests. * BAEL-838 Added new line examples. * BAEL-838 Renamed RemoveLastChar class to StringHelper and added Java8 examples. Refactord code. * BAEL-838 Changed method names * BAEL-838 Tiny change to keep code consistant. Return null or empty. * BAEL-838 Removed unresolved conflict. * BAEL-821 New class that shows different rounding techniques. Updated POM. * BAEL-821 - Added unit test for different round methods. * BAEL-821 Changed test method name to follow the convention * BAEL-821 Added more test and updated round methods. * BAEL-837 - initial commit. A few examples of adding doubles. * BAEL-837 - Couple of smaller changes * BAEL-837 - Added jUnit test. * BAEL-579 Updated Spring Cloud Version I was getting error: java.lang.NoSuchMethodError: org.springframework.cloud.config.environment.Environment After version update, all is okay. * BAEL-579 Added actuator to Cloud Config Client. * BAEL-579 Enabled cloud bus and updated dependencies. * BAEL-579 Config Client using Spring Cloud Bus. * BAEL-579 Recreated Basic Config Server. * BAEL-579 Recreated Config Client. * BAEL-579 Removed test Git URL. * BAEL-579 Added Actuator to Config Client * BAEL-579 Added Spring Cloud Bus to Client. * BAEL-579 Server changes for Spring Cloud Bus Added dependencies and removed git.clone-on-start as this was causing server to throw errors after git properties change. * BAEL-579 Removed Git URL. * Revert "BAEL-579 Updated Spring Cloud Version" This reverts commit f775bf91e53a1ecfb9b70596688d7c8202bf495f. * Revert "BAEL-579 Config Client using Spring Cloud Bus." This reverts commit 1d96bc5761994a33af9a7a9aa5ab68604a5b44dc. * Revert "BAEL-579 Enabled cloud bus and updated dependencies." This reverts commit 7845da922d89d53506dd0fff387ea13694c50bc1. * Revert "BAEL-579 Added actuator to Cloud Config Client." This reverts commit 076657a26a57e0aa676989a4d97966a3b9d53e1c. * BAEL-579 Added missing dependency versions. * BAEL-579 Added missing dependency versions. * Updated gitignore * BAEL-1065 Simple performance check StringBuffer vs StringBuilder. * BAEL-1065 Added JMH benchmarks * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Dancing Links Algorithm. Smaller changes to Backtracking * BAEL-1298 Resolve conflict - use most up-to-date POM * Updated code - mostly with CONSTANTS. Extracted methods. * Removed pointless Java8 code. Renamed constant --- .../sudoku/BacktrackingAlgorithm.java | 103 ++++++++++++++ .../algorithms/sudoku/ColumnNode.java | 33 +++++ .../algorithms/sudoku/DancingLinks.java | 134 ++++++++++++++++++ .../sudoku/DancingLinksAlgorithm.java | 110 ++++++++++++++ .../algorithms/sudoku/DancingNode.java | 50 +++++++ 5 files changed, 430 insertions(+) create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java new file mode 100644 index 0000000000..127e78900c --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java @@ -0,0 +1,103 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.stream.IntStream; + +public class BacktrackingAlgorithm { + + private static int BOARD_SIZE = 9; + private static int SUBSECTION_SIZE = 3; + private static int BOARD_START_INDEX = 0; + + private static int NO_VALUE = 0; + private static int MIN_VALUE = 1; + private static int MAX_VALUE = 9; + + public static int[][] board = { + { 8, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 3, 6, 0, 0, 0, 0, 0 }, + { 0, 7, 0, 0, 9, 0, 2, 0, 0 }, + { 0, 5, 0, 0, 0, 7, 0, 0, 0 }, + { 0, 0, 0, 0, 4, 5, 7, 0, 0 }, + { 0, 0, 0, 1, 0, 0, 0, 3, 0 }, + { 0, 0, 1, 0, 0, 0, 0, 6, 8 }, + { 0, 0, 8, 5, 0, 0, 0, 1, 0 }, + { 0, 9, 0, 0, 0, 0, 4, 0, 0 } + }; + + public static void main(String[] args) { + BacktrackingAlgorithm solver = new BacktrackingAlgorithm(); + solver.solve(board); + solver.printBoard(); + } + + public void printBoard() { + for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) { + for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) { + System.out.print(board[row][column] + " "); + } + System.out.println(); + } + } + + public boolean solve(int[][] board) { + for (int r = BOARD_START_INDEX; r < BOARD_SIZE; r++) { + for (int c = BOARD_START_INDEX; c < BOARD_SIZE; c++) { + if (board[r][c] == NO_VALUE) { + for (int k = MIN_VALUE; k <= MAX_VALUE; k++) { + board[r][c] = k; + if (isValid(board, r, c) && solve(board)) { + return true; + } else { + board[r][c] = NO_VALUE; + } + } + return false; + } + } + } + return true; + } + + public boolean isValid(int[][] board, int r, int c) { + return (rowConstraint(board, r) && + columnConstraint(board, c) && + subsectionConstraint(board, r, c)); + } + + private boolean subsectionConstraint(int[][] board, int r, int c) { + boolean[] constraint = new boolean[BOARD_SIZE]; + for (int i = (r / SUBSECTION_SIZE) * SUBSECTION_SIZE; i < (r / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; i++) { + for (int j = (c / SUBSECTION_SIZE) * SUBSECTION_SIZE; j < (c / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; j++) { + if (!checkConstraint(board, i, constraint, j)) return false; + } + } + return true; + } + + private boolean columnConstraint(int[][] board, int c) { + boolean[] constraint = new boolean[BOARD_SIZE]; + for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) { + if (!checkConstraint(board, i, constraint, c)) return false; + } + return true; + } + + private boolean rowConstraint(int[][] board, int r) { + boolean[] constraint = new boolean[BOARD_SIZE]; + for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) { + if (!checkConstraint(board, r, constraint, i)) return false; + } + return true; + } + + private boolean checkConstraint(int[][] board, int r, boolean[] constraint, int c) { + if (board[r][c] >= MIN_VALUE && board[r][c] <= MAX_VALUE) { + if (constraint[board[r][c] - 1] == false) { + constraint[board[r][c] - 1] = true; + } else { + return false; + } + } + return true; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java new file mode 100644 index 0000000000..48538344b6 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java @@ -0,0 +1,33 @@ +package com.baeldung.algorithms.sudoku; + +class ColumnNode extends DancingNode { + int size; + String name; + + public ColumnNode(String n) { + super(); + size = 0; + name = n; + C = this; + } + + void cover() { + unlinkLR(); + for (DancingNode i = this.D; i != this; i = i.D) { + for (DancingNode j = i.R; j != i; j = j.R) { + j.unlinkUD(); + j.C.size--; + } + } + } + + void uncover() { + for (DancingNode i = this.U; i != this; i = i.U) { + for (DancingNode j = i.L; j != i; j = j.L) { + j.C.size++; + j.relinkUD(); + } + } + relinkLR(); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java new file mode 100644 index 0000000000..a30f8ecab5 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java @@ -0,0 +1,134 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class DancingLinks { + + private ColumnNode header; + private List answer; + + private void search(int k) { + if (header.R == header) { + handleSolution(answer); + } else { + ColumnNode c = selectColumnNodeHeuristic(); + c.cover(); + + for (DancingNode r = c.D; r != c; r = r.D) { + answer.add(r); + + for (DancingNode j = r.R; j != r; j = j.R) { + j.C.cover(); + } + + search(k + 1); + + r = answer.remove(answer.size() - 1); + c = r.C; + + for (DancingNode j = r.L; j != r; j = j.L) { + j.C.uncover(); + } + } + c.uncover(); + } + } + + private ColumnNode selectColumnNodeHeuristic() { + int min = Integer.MAX_VALUE; + ColumnNode ret = null; + for (ColumnNode c = (ColumnNode) header.R; c != header; c = (ColumnNode) c.R) { + if (c.size < min) { + min = c.size; + ret = c; + } + } + return ret; + } + + private ColumnNode makeDLXBoard(boolean[][] grid) { + final int COLS = grid[0].length; + final int ROWS = grid.length; + + ColumnNode headerNode = new ColumnNode("header"); + ArrayList columnNodes = new ArrayList(); + + for (int i = 0; i < COLS; i++) { + ColumnNode n = new ColumnNode(Integer.toString(i)); + columnNodes.add(n); + headerNode = (ColumnNode) headerNode.hookRight(n); + } + headerNode = headerNode.R.C; + + for (int i = 0; i < ROWS; i++) { + DancingNode prev = null; + for (int j = 0; j < COLS; j++) { + if (grid[i][j] == true) { + ColumnNode col = columnNodes.get(j); + DancingNode newNode = new DancingNode(col); + if (prev == null) + prev = newNode; + col.U.hookDown(newNode); + prev = prev.hookRight(newNode); + col.size++; + } + } + } + + headerNode.size = COLS; + + return headerNode; + } + + public DancingLinks(boolean[][] cover) { + header = makeDLXBoard(cover); + } + + public void runSolver() { + answer = new LinkedList(); + search(0); + } + + public void handleSolution(List answer) { + int[][] result = parseBoard(answer); + printSolution(result); + } + + int size = 9; + + private int[][] parseBoard(List answer) { + int[][] result = new int[size][size]; + for (DancingNode n : answer) { + DancingNode rcNode = n; + int min = Integer.parseInt(rcNode.C.name); + for (DancingNode tmp = n.R; tmp != n; tmp = tmp.R) { + int val = Integer.parseInt(tmp.C.name); + if (val < min) { + min = val; + rcNode = tmp; + } + } + int ans1 = Integer.parseInt(rcNode.C.name); + int ans2 = Integer.parseInt(rcNode.R.C.name); + int r = ans1 / size; + int c = ans1 % size; + int num = (ans2 % size) + 1; + result[r][c] = num; + } + return result; + } + + public static void printSolution(int[][] result) { + int N = result.length; + for (int i = 0; i < N; i++) { + String ret = ""; + for (int j = 0; j < N; j++) { + ret += result[i][j] + " "; + } + System.out.println(ret); + } + System.out.println(); + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java new file mode 100644 index 0000000000..057a15c594 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java @@ -0,0 +1,110 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.*; + +public class DancingLinksAlgorithm { + private static int BOARD_SIZE = 9; + private static int SUBSECTION_SIZE = 3; + private static int NO_VALUE = 0; + private static int CONSTRAINTS = 4; + private static int MIN_VALUE = 1; + private static int MAX_VALUE = 9; + private static int COVER_START_INDEX = 1; + + public static int[][] board = { + { 8, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 3, 6, 0, 0, 0, 0, 0 }, + { 0, 7, 0, 0, 9, 0, 2, 0, 0 }, + { 0, 5, 0, 0, 0, 7, 0, 0, 0 }, + { 0, 0, 0, 0, 4, 5, 7, 0, 0 }, + { 0, 0, 0, 1, 0, 0, 0, 3, 0 }, + { 0, 0, 1, 0, 0, 0, 0, 6, 8 }, + { 0, 0, 8, 5, 0, 0, 0, 1, 0 }, + { 0, 9, 0, 0, 0, 0, 4, 0, 0 } + }; + + public static void main(String[] args) { + DancingLinksAlgorithm solver = new DancingLinksAlgorithm(); + solver.solve(board); + } + + public boolean solve(int[][] board) { + boolean[][] cover = initializeExactCoverBoard(board); + DancingLinks dlx = new DancingLinks(cover); + dlx.runSolver(); + + return true; + } + + private int getIndex(int row, int col, int num) { + return (row - 1) * BOARD_SIZE * BOARD_SIZE + (col - 1) * BOARD_SIZE + (num - 1); + } + + private boolean[][] createExactCoverBoard() { + boolean[][] R = new boolean[BOARD_SIZE * BOARD_SIZE * MAX_VALUE][BOARD_SIZE * BOARD_SIZE * CONSTRAINTS]; + + int hBase = 0; + + // Cell constraint. + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++, hBase++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++) { + int index = getIndex(r, c, n); + R[index][hBase] = true; + } + } + } + + // Row constrain. + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int c1 = COVER_START_INDEX; c1 <= BOARD_SIZE; c1++) { + int index = getIndex(r, c1, n); + R[index][hBase] = true; + } + } + } + + // Column constraint. + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int r1 = COVER_START_INDEX; r1 <= BOARD_SIZE; r1++) { + int index = getIndex(r1, c, n); + R[index][hBase] = true; + } + } + } + + // Subsection constraint + for (int br = COVER_START_INDEX; br <= BOARD_SIZE; br += SUBSECTION_SIZE) { + for (int bc = COVER_START_INDEX; bc <= BOARD_SIZE; bc += SUBSECTION_SIZE) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int rDelta = 0; rDelta < SUBSECTION_SIZE; rDelta++) { + for (int cDelta = 0; cDelta < SUBSECTION_SIZE; cDelta++) { + int index = getIndex(br + rDelta, bc + cDelta, n); + R[index][hBase] = true; + } + } + } + } + } + return R; + } + + private boolean[][] initializeExactCoverBoard(int[][] board) { + boolean[][] R = createExactCoverBoard(); + for (int i = COVER_START_INDEX; i <= BOARD_SIZE; i++) { + for (int j = COVER_START_INDEX; j <= BOARD_SIZE; j++) { + int n = board[i - 1][j - 1]; + if (n != NO_VALUE) { + for (int num = MIN_VALUE; num <= MAX_VALUE; num++) { + if (num != n) { + Arrays.fill(R[getIndex(i, j, num)], false); + } + } + } + } + } + return R; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java new file mode 100644 index 0000000000..13dc3f2b57 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java @@ -0,0 +1,50 @@ +package com.baeldung.algorithms.sudoku; + +class DancingNode { + DancingNode L, R, U, D; + ColumnNode C; + + DancingNode hookDown(DancingNode n1) { + assert (this.C == n1.C); + n1.D = this.D; + n1.D.U = n1; + n1.U = this; + this.D = n1; + return n1; + } + + DancingNode hookRight(DancingNode n1) { + n1.R = this.R; + n1.R.L = n1; + n1.L = this; + this.R = n1; + return n1; + } + + void unlinkLR() { + this.L.R = this.R; + this.R.L = this.L; + } + + void relinkLR() { + this.L.R = this.R.L = this; + } + + void unlinkUD() { + this.U.D = this.D; + this.D.U = this.U; + } + + void relinkUD() { + this.U.D = this.D.U = this; + } + + public DancingNode() { + L = R = U = D = this; + } + + public DancingNode(ColumnNode c) { + this(); + C = c; + } +} \ No newline at end of file From a48e0625987442bd490766828d10187afc1ebbce Mon Sep 17 00:00:00 2001 From: mkuligowski Date: Tue, 30 Jan 2018 22:49:54 +0100 Subject: [PATCH 078/118] BAEL-1204 (#3508) * Initialize smooks subproject * Add Smooks dependency * Delete files form badly created submodule * Add domain classes * Create class responsible for converting Orders * Create class responsible for validating messages * Add configuration file * Add integration tests for Smooks converters and validators * ADd en_US locale and fix date format * Fix number format in expected messages * Delete unused mapping * Remove unused conversion to JSON * Add assertion for ruleName in givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors --- libraries/pom.xml | 6 ++ .../smooks/converter/OrderConverter.java | 45 ++++++++++++ .../smooks/converter/OrderValidator.java | 27 +++++++ .../java/com/baeldung/smooks/model/Item.java | 71 +++++++++++++++++++ .../java/com/baeldung/smooks/model/Order.java | 52 ++++++++++++++ .../com/baeldung/smooks/model/Status.java | 5 ++ .../com/baeldung/smooks/model/Supplier.java | 49 +++++++++++++ libraries/src/main/resources/smooks/email.ftl | 8 +++ .../src/main/resources/smooks/item-rules.csv | 1 + libraries/src/main/resources/smooks/order.ftl | 7 ++ .../src/main/resources/smooks/order.json | 21 ++++++ libraries/src/main/resources/smooks/order.xml | 20 ++++++ .../main/resources/smooks/smooks-mapping.xml | 29 ++++++++ .../resources/smooks/smooks-transform-edi.xml | 11 +++ .../smooks/smooks-transform-email.xml | 12 ++++ .../resources/smooks/smooks-validation.xml | 17 +++++ .../main/resources/smooks/supplier.properties | 2 + .../converter/SmooksIntegrationTest.java | 70 ++++++++++++++++++ 18 files changed, 453 insertions(+) create mode 100644 libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java create mode 100644 libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java create mode 100644 libraries/src/main/java/com/baeldung/smooks/model/Item.java create mode 100644 libraries/src/main/java/com/baeldung/smooks/model/Order.java create mode 100644 libraries/src/main/java/com/baeldung/smooks/model/Status.java create mode 100644 libraries/src/main/java/com/baeldung/smooks/model/Supplier.java create mode 100644 libraries/src/main/resources/smooks/email.ftl create mode 100644 libraries/src/main/resources/smooks/item-rules.csv create mode 100644 libraries/src/main/resources/smooks/order.ftl create mode 100644 libraries/src/main/resources/smooks/order.json create mode 100644 libraries/src/main/resources/smooks/order.xml create mode 100644 libraries/src/main/resources/smooks/smooks-mapping.xml create mode 100644 libraries/src/main/resources/smooks/smooks-transform-edi.xml create mode 100644 libraries/src/main/resources/smooks/smooks-transform-email.xml create mode 100644 libraries/src/main/resources/smooks/smooks-validation.xml create mode 100644 libraries/src/main/resources/smooks/supplier.properties create mode 100644 libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java diff --git a/libraries/pom.xml b/libraries/pom.xml index 3e802e76c8..a330494bb3 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -710,6 +710,11 @@ test test + + org.milyn + milyn-smooks-all + ${smooks.version} + @@ -789,6 +794,7 @@ 1.23.0 v4-rev493-1.21.0 1.0.0 + 1.7.0 3.0.14 \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java new file mode 100644 index 0000000000..d11f5a29b2 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java @@ -0,0 +1,45 @@ +package com.baeldung.smooks.converter; + +import com.baeldung.smooks.model.Order; +import org.milyn.Smooks; +import org.milyn.payload.JavaResult; +import org.milyn.payload.StringResult; +import org.xml.sax.SAXException; + +import javax.xml.transform.stream.StreamSource; +import java.io.IOException; + +public class OrderConverter { + + public Order convertOrderXMLToOrderObject(String path) throws IOException, SAXException { + Smooks smooks = new Smooks(OrderConverter.class.getResourceAsStream("/smooks/smooks-mapping.xml")); + try { + JavaResult javaResult = new JavaResult(); + smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), javaResult); + return (Order) javaResult.getBean("order"); + } finally { + smooks.close(); + } + } + + + public String convertOrderXMLtoEDIFACT(String path) throws IOException, SAXException { + return convertDocumentWithTempalte(path, "/smooks/smooks-transform-edi.xml"); + } + + public String convertOrderXMLtoEmailMessage(String path) throws IOException, SAXException { + return convertDocumentWithTempalte(path, "/smooks/smooks-transform-email.xml"); + } + + private String convertDocumentWithTempalte(String path, String config) throws IOException, SAXException { + Smooks smooks = new Smooks(config); + + try { + StringResult stringResult = new StringResult(); + smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), stringResult); + return stringResult.toString(); + } finally { + smooks.close(); + } + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java new file mode 100644 index 0000000000..3975921da0 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java @@ -0,0 +1,27 @@ +package com.baeldung.smooks.converter; + +import org.milyn.Smooks; +import org.milyn.payload.JavaResult; +import org.milyn.payload.StringResult; +import org.milyn.validation.ValidationResult; +import org.xml.sax.SAXException; + +import javax.xml.transform.stream.StreamSource; +import java.io.IOException; + +public class OrderValidator { + + public ValidationResult validate(String path) throws IOException, SAXException { + Smooks smooks = new Smooks(OrderValidator.class.getResourceAsStream("/smooks/smooks-validation.xml")); + + try { + StringResult xmlResult = new StringResult(); + JavaResult javaResult = new JavaResult(); + ValidationResult validationResult = new ValidationResult(); + smooks.filterSource(new StreamSource(OrderValidator.class.getResourceAsStream(path)), xmlResult, javaResult, validationResult); + return validationResult; + } finally { + smooks.close(); + } + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Item.java b/libraries/src/main/java/com/baeldung/smooks/model/Item.java new file mode 100644 index 0000000000..a7f7783b3f --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Item.java @@ -0,0 +1,71 @@ +package com.baeldung.smooks.model; + +public class Item { + + public Item() { + } + + public Item(String code, Double price, Integer quantity) { + this.code = code; + this.price = price; + this.quantity = quantity; + } + + private String code; + private Double price; + private Integer quantity; + + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Item item = (Item) o; + + if (code != null ? !code.equals(item.code) : item.code != null) return false; + if (price != null ? !price.equals(item.price) : item.price != null) return false; + return quantity != null ? quantity.equals(item.quantity) : item.quantity == null; + } + + @Override + public int hashCode() { + int result = code != null ? code.hashCode() : 0; + result = 31 * result + (price != null ? price.hashCode() : 0); + result = 31 * result + (quantity != null ? quantity.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Item{" + + "code='" + code + '\'' + + ", price=" + price + + ", quantity=" + quantity + + '}'; + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Order.java b/libraries/src/main/java/com/baeldung/smooks/model/Order.java new file mode 100644 index 0000000000..047e1fe8a3 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Order.java @@ -0,0 +1,52 @@ +package com.baeldung.smooks.model; + +import java.util.Date; +import java.util.List; + +public class Order { + private Date creationDate; + private Long number; + private Status status; + private Supplier supplier; + private List items; + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public Long getNumber() { + return number; + } + + public void setNumber(Long number) { + this.number = number; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public Supplier getSupplier() { + return supplier; + } + + public void setSupplier(Supplier supplier) { + this.supplier = supplier; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Status.java b/libraries/src/main/java/com/baeldung/smooks/model/Status.java new file mode 100644 index 0000000000..53c50bdf46 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Status.java @@ -0,0 +1,5 @@ +package com.baeldung.smooks.model; + +public enum Status { + NEW, IN_PROGRESS, FINISHED +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java new file mode 100644 index 0000000000..31a9e1f43f --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java @@ -0,0 +1,49 @@ +package com.baeldung.smooks.model; + +public class Supplier { + + private String name; + private String phoneNumber; + + public Supplier() { + } + + public Supplier(String name, String phoneNumber) { + this.name = name; + this.phoneNumber = phoneNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Supplier supplier = (Supplier) o; + + if (name != null ? !name.equals(supplier.name) : supplier.name != null) return false; + return phoneNumber != null ? phoneNumber.equals(supplier.phoneNumber) : supplier.phoneNumber == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0); + return result; + } +} diff --git a/libraries/src/main/resources/smooks/email.ftl b/libraries/src/main/resources/smooks/email.ftl new file mode 100644 index 0000000000..8413046508 --- /dev/null +++ b/libraries/src/main/resources/smooks/email.ftl @@ -0,0 +1,8 @@ +<#setting locale="en_US"> +Hi, +Order number #${order.number} created on ${order.creationDate?string["yyyy-MM-dd"]} is currently in ${order.status} status. +Consider contact supplier "${supplier.name}" with phone number: "${supplier.phoneNumber}". +Order items: +<#list items as item> +${item.quantity} X ${item.code} (total price ${item.price * item.quantity}) + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/item-rules.csv b/libraries/src/main/resources/smooks/item-rules.csv new file mode 100644 index 0000000000..c93c453f25 --- /dev/null +++ b/libraries/src/main/resources/smooks/item-rules.csv @@ -0,0 +1 @@ +"max_total","item.quantity * item.price < 300.00" diff --git a/libraries/src/main/resources/smooks/order.ftl b/libraries/src/main/resources/smooks/order.ftl new file mode 100644 index 0000000000..9d40eb55c7 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.ftl @@ -0,0 +1,7 @@ +<#setting locale="en_US"> +UNA:+.? ' +UNH+${order.number}+${order.status}+${order.creationDate?string["yyyy-MM-dd"]}' +CTA+${supplier.name}+${supplier.phoneNumber}' +<#list items as item> +LIN+${item.quantity}+${item.code}+${item.price}' + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/order.json b/libraries/src/main/resources/smooks/order.json new file mode 100644 index 0000000000..bf6bc5fe93 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.json @@ -0,0 +1,21 @@ +{ + "creationDate":"2018-01-14", + "orderNumber":771, + "orderStatus":"IN_PROGRESS", + "supplier":{ + "name":"CompanyX", + "phone":"1234567" + }, + "orderItems":[ + { + "quantity":1, + "code":"PX1234", + "price":9.99 + }, + { + "quantity":2, + "code":"RX1990", + "price":120.32 + } + ] +} \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/order.xml b/libraries/src/main/resources/smooks/order.xml new file mode 100644 index 0000000000..343c5cab38 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.xml @@ -0,0 +1,20 @@ + + 771 + IN_PROGRESS + + CompanyX + 1234567 + + + + 1 + PX1234 + 9.99 + + + 2 + RX990 + 120.32 + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-mapping.xml b/libraries/src/main/resources/smooks/smooks-mapping.xml new file mode 100644 index 0000000000..7996834e38 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-mapping.xml @@ -0,0 +1,29 @@ + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-transform-edi.xml b/libraries/src/main/resources/smooks/smooks-transform-edi.xml new file mode 100644 index 0000000000..1dae4055a8 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-transform-edi.xml @@ -0,0 +1,11 @@ + + + + + + + /smooks/order.ftl + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-transform-email.xml b/libraries/src/main/resources/smooks/smooks-transform-email.xml new file mode 100644 index 0000000000..101aa67f0d --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-transform-email.xml @@ -0,0 +1,12 @@ + + + + + + + + /smooks/email.ftl + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-validation.xml b/libraries/src/main/resources/smooks/smooks-validation.xml new file mode 100644 index 0000000000..b66722ffc5 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-validation.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/libraries/src/main/resources/smooks/supplier.properties b/libraries/src/main/resources/smooks/supplier.properties new file mode 100644 index 0000000000..cc17e45eb4 --- /dev/null +++ b/libraries/src/main/resources/smooks/supplier.properties @@ -0,0 +1,2 @@ +supplierName=[A-Za-z0-9]* +supplierPhone=^[0-9\\-\\+]{9,15}$ diff --git a/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java new file mode 100644 index 0000000000..4d2cb71329 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java @@ -0,0 +1,70 @@ +package com.baeldung.smooks.converter; + +import com.baeldung.smooks.model.Item; +import com.baeldung.smooks.model.Order; +import com.baeldung.smooks.model.Status; +import com.baeldung.smooks.model.Supplier; +import org.junit.Test; +import org.milyn.validation.ValidationResult; +import java.text.SimpleDateFormat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + + +public class SmooksIntegrationTest { + + private static final String EDIFACT_MESSAGE = + "UNA:+.? '\r\n" + + "UNH+771+IN_PROGRESS+2018-01-14'\r\n" + + "CTA+CompanyX+1234567'\r\n" + + "LIN+1+PX1234+9.99'\r\n" + + "LIN+2+RX990+120.32'\r\n"; + private static final String EMAIL_MESSAGE = + "Hi,\r\n" + + "Order number #771 created on 2018-01-14 is currently in IN_PROGRESS status.\r\n" + + "Consider contact supplier \"CompanyX\" with phone number: \"1234567\".\r\n" + + "Order items:\r\n" + + "1 X PX1234 (total price 9.99)\r\n" + + "2 X RX990 (total price 240.64)\r\n"; + + @Test + public void givenOrderXML_whenConvert_thenPOJOsConstructedCorrectly() throws Exception { + + OrderConverter xmlToJavaOrderConverter = new OrderConverter(); + Order order = xmlToJavaOrderConverter.convertOrderXMLToOrderObject("/smooks/order.xml"); + + assertThat(order.getNumber(),is(771L)); + assertThat(order.getStatus(),is(Status.IN_PROGRESS)); + assertThat(order.getCreationDate(),is(new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-14"))); + assertThat(order.getSupplier(),is(new Supplier("CompanyX","1234567"))); + assertThat(order.getItems(),containsInAnyOrder( + new Item("PX1234",9.99,1), + new Item("RX990",120.32,2)) + ); + + } + + @Test + public void givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors() throws Exception { + OrderValidator orderValidator = new OrderValidator(); + ValidationResult validationResult = orderValidator.validate("/smooks/order.xml"); + + assertThat(validationResult.getErrors(), hasSize(1)); + // 1234567 didn't match ^[0-9\\-\\+]{9,15}$ + assertThat(validationResult.getErrors().get(0).getFailRuleResult().getRuleName(),is("supplierPhone")); + } + + @Test + public void givenOrderXML_whenApplyEDITemplate_thenConvertedToEDIFACT() throws Exception { + OrderConverter orderConverter = new OrderConverter(); + String edifact = orderConverter.convertOrderXMLtoEDIFACT("/smooks/order.xml"); + assertThat(edifact,is(EDIFACT_MESSAGE)); + } + + @Test + public void givenOrderXML_whenApplyEmailTemplate_thenConvertedToEmailMessage() throws Exception { + OrderConverter orderConverter = new OrderConverter(); + String emailMessage = orderConverter.convertOrderXMLtoEmailMessage("/smooks/order.xml"); + assertThat(emailMessage,is(EMAIL_MESSAGE)); + } +} \ No newline at end of file From eb99ad575377f17e81e0cb480bcd4d48e234204c Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Wed, 31 Jan 2018 16:32:21 +0700 Subject: [PATCH 079/118] Initial commit for BAEL-1515 --- testing-modules/testing/pom.xml | 12 ++++++ .../testing/assertj/custom/Assertions.java | 11 ++++++ .../baeldung/testing/assertj/custom/Car.java | 22 +++++++++++ .../testing/assertj/custom/CarAssert.java | 30 +++++++++++++++ .../testing/assertj/custom/Person.java | 32 ++++++++++++++++ .../testing/assertj/custom/PersonAssert.java | 38 +++++++++++++++++++ .../custom/AssertJCarAssertUnitTest.java | 20 ++++++++++ .../AssertJCustomAssertionsUnitTest.java | 29 ++++++++++++++ .../custom/AssertJPersonAssertUnitTest.java | 26 +++++++++++++ 9 files changed, 220 insertions(+) create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 1fd6357b87..6f185d3b4c 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -154,6 +154,17 @@ + + org.assertj + assertj-assertions-generator-maven-plugin + ${assertj-generator.version} + + + com.baeldung.testing.assertj.custom.Person + com.baeldung.testing.assertj.custom.Car + + + @@ -164,6 +175,7 @@ 21.0 3.1.0 3.6.1 + 2.1.0 0.32 1.1.0 0.12 diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java new file mode 100644 index 0000000000..5c72eb6d05 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java @@ -0,0 +1,11 @@ +package com.baeldung.testing.assertj.custom; + +public class Assertions { + public static PersonAssert assertThat(Person actual) { + return new PersonAssert(actual); + } + + public static CarAssert assertThat(Car actual) { + return new CarAssert(actual); + } +} diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java new file mode 100644 index 0000000000..e52ffee8e5 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java @@ -0,0 +1,22 @@ +package com.baeldung.testing.assertj.custom; + +public class Car { + private String type; + private Person owner; + + public Car(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public Person getOwner() { + return owner; + } + + public void setOwner(Person owner) { + this.owner = owner; + } +} diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java new file mode 100644 index 0000000000..413c2d3e12 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java @@ -0,0 +1,30 @@ +package com.baeldung.testing.assertj.custom; + +import org.assertj.core.api.AbstractAssert; + +public class CarAssert extends AbstractAssert { + + public CarAssert(Car actual) { + super(actual, CarAssert.class); + } + + public static CarAssert assertThat(Car actual) { + return new CarAssert(actual); + } + + public CarAssert hasType(String type) { + isNotNull(); + if (!actual.getType().equals(type)) { + failWithMessage("Expected type %s but was %s", type, actual.getType()); + } + return this; + } + + public CarAssert isUsed() { + isNotNull(); + if (actual.getOwner() == null) { + failWithMessage("Expected old but was new"); + } + return this; + } +} diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java new file mode 100644 index 0000000000..34afc480e4 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java @@ -0,0 +1,32 @@ +package com.baeldung.testing.assertj.custom; + +import java.util.ArrayList; +import java.util.List; + +public class Person { + private String fullName; + private int age; + private List nicknames; + + public Person(String fullName, int age) { + this.fullName = fullName; + this.age = age; + this.nicknames = new ArrayList<>(); + } + + public void addNickname(String nickname) { + nicknames.add(nickname); + } + + public String getFullName() { + return fullName; + } + + public int getAge() { + return age; + } + + public List getNicknames() { + return nicknames; + } +} diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java new file mode 100644 index 0000000000..4c071660f3 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java @@ -0,0 +1,38 @@ +package com.baeldung.testing.assertj.custom; + +import org.assertj.core.api.AbstractAssert; + +public class PersonAssert extends AbstractAssert { + + public PersonAssert(Person actual) { + super(actual, PersonAssert.class); + } + + public static PersonAssert assertThat(Person actual) { + return new PersonAssert(actual); + } + + public PersonAssert hasFullName(String fullName) { + isNotNull(); + if (!actual.getFullName().equals(fullName)) { + failWithMessage("Expected full name %s but was %s", fullName, actual.getFullName()); + } + return this; + } + + public PersonAssert isAdult() { + isNotNull(); + if (actual.getAge() < 18) { + failWithMessage("Expected adult but was juvenile"); + } + return this; + } + + public PersonAssert hasNickname(String nickName) { + isNotNull(); + if (!actual.getNicknames().contains(nickName)) { + failWithMessage("Expected nickname %s but did not have", nickName); + } + return this; + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java new file mode 100644 index 0000000000..d438cc42f6 --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java @@ -0,0 +1,20 @@ +package com.baeldung.testing.assertj.custom; + +import static com.baeldung.testing.assertj.custom.CarAssert.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class AssertJCarAssertUnitTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void whenCarTypeDoesNotMatch_thenIncorrect() { + thrown.expect(AssertionError.class); + thrown.expectMessage("Expected type SUV but was Sedan"); + Car car = new Car("Sedan"); + assertThat(car).hasType("SUV"); + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java new file mode 100644 index 0000000000..8b800de3db --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.testing.assertj.custom; + +import static com.baeldung.testing.assertj.custom.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class AssertJCustomAssertionsUnitTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void whenPersonDoesNotHaveAMatchingNickname_thenIncorrect() { + thrown.expect(AssertionError.class); + thrown.expectMessage("Expected nickname John but did not have"); + Person person = new Person("John Doe", 20); + person.addNickname("Nick"); + assertThat(person).hasNickname("John"); + } + + @Test + public void whenCarIsUsed_thenCorrect() { + Person person = new Person("Jane Roe", 16); + Car car = new Car("SUV"); + car.setOwner(person); + assertThat(car).isUsed(); + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java new file mode 100644 index 0000000000..ab421915af --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java @@ -0,0 +1,26 @@ +package com.baeldung.testing.assertj.custom; + +import static com.baeldung.testing.assertj.custom.PersonAssert.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class AssertJPersonAssertUnitTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void whenPersonNameMatches_thenCorrect() { + Person person = new Person("John Doe", 20); + assertThat(person).hasFullName("John Doe"); + } + + @Test + public void whenPersonAgeLessThanEighteen_thenNotAdult() { + thrown.expect(AssertionError.class); + thrown.expectMessage("Expected adult but was juvenile"); + Person person = new Person("Jane Roe", 16); + assertThat(person).isAdult(); + } +} From 950434a87341b4a6f1085b8feb8bb33eb95447f2 Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Wed, 31 Jan 2018 16:46:19 +0700 Subject: [PATCH 080/118] Fix the scope of AssertJ core --- testing-modules/testing/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 6f185d3b4c..2804a94244 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -34,7 +34,6 @@ org.assertj assertj-core ${assertj-core.version} - test From 5f87ceb1ffcdaf64f9d4ecf8ee468511c22a9f92 Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Wed, 31 Jan 2018 18:34:09 +0700 Subject: [PATCH 081/118] Relocate custom assertion classes --- testing-modules/testing/pom.xml | 1 + .../java/com/baeldung/testing/assertj/custom/Assertions.java | 0 .../java/com/baeldung/testing/assertj/custom/CarAssert.java | 0 .../java/com/baeldung/testing/assertj/custom/PersonAssert.java | 0 4 files changed, 1 insertion(+) rename testing-modules/testing/src/{main => test}/java/com/baeldung/testing/assertj/custom/Assertions.java (100%) rename testing-modules/testing/src/{main => test}/java/com/baeldung/testing/assertj/custom/CarAssert.java (100%) rename testing-modules/testing/src/{main => test}/java/com/baeldung/testing/assertj/custom/PersonAssert.java (100%) diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 2804a94244..6f185d3b4c 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -34,6 +34,7 @@ org.assertj assertj-core ${assertj-core.version} + test diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java similarity index 100% rename from testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Assertions.java rename to testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java similarity index 100% rename from testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/CarAssert.java rename to testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java similarity index 100% rename from testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/PersonAssert.java rename to testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java From bd9a87c137477b26047f9e033e5fa6f049a534ba Mon Sep 17 00:00:00 2001 From: Aprian Diaz Novandi Date: Wed, 31 Jan 2018 15:58:52 +0100 Subject: [PATCH 082/118] Simplify unit test logic (#3548) --- .../baeldung/guava/GuavaMemoizerUnitTest.java | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java index 0ae1f438e3..1f934347b4 100644 --- a/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java +++ b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java @@ -38,7 +38,7 @@ public class GuavaMemoizerUnitTest { int n = 95; // when - BigInteger factorial = new Factorial().getFactorial(n); + BigInteger factorial = Factorial.getFactorial(n); // then BigInteger expectedFactorial = new BigInteger("10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000"); @@ -47,52 +47,46 @@ public class GuavaMemoizerUnitTest { @Test public void givenMemoizedSupplier_whenGet_thenSubsequentGetsAreFast() { + // given Supplier memoizedSupplier; memoizedSupplier = Suppliers.memoize(CostlySupplier::generateBigNumber); - Instant start = Instant.now(); - BigInteger bigNumber = memoizedSupplier.get(); - Long durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12345")))); - assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); + // when + BigInteger expectedValue = new BigInteger("12345"); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); - start = Instant.now(); - bigNumber = memoizedSupplier.get().add(BigInteger.ONE); - durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12346")))); - assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); - - start = Instant.now(); - bigNumber = memoizedSupplier.get().add(BigInteger.TEN); - durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12355")))); - assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); + // then + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); } @Test public void givenMemoizedSupplierWithExpiration_whenGet_thenSubsequentGetsBeforeExpiredAreFast() throws InterruptedException { + // given Supplier memoizedSupplier; memoizedSupplier = Suppliers.memoizeWithExpiration(CostlySupplier::generateBigNumber, 3, TimeUnit.SECONDS); - Instant start = Instant.now(); - BigInteger bigNumber = memoizedSupplier.get(); - Long durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12345")))); - assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); - - start = Instant.now(); - bigNumber = memoizedSupplier.get().add(BigInteger.ONE); - durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12346")))); - assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D))); + // when + BigInteger expectedValue = new BigInteger("12345"); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); + // then + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); + // add one more second until memoized Supplier is evicted from memory TimeUnit.SECONDS.sleep(1); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); + } - start = Instant.now(); - bigNumber = memoizedSupplier.get().add(BigInteger.TEN); - durationInMs = Duration.between(start, Instant.now()).toMillis(); - assertThat(bigNumber, is(equalTo(new BigInteger("12355")))); - assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D))); + private void assertSupplierGetExecutionResultAndDuration(Supplier supplier, + T expectedValue, + double expectedDurationInMs) { + Instant start = Instant.now(); + T value = supplier.get(); + Long durationInMs = Duration.between(start, Instant.now()).toMillis(); + double marginOfErrorInMs = 100D; + + assertThat(value, is(equalTo(expectedValue))); + assertThat(durationInMs.doubleValue(), is(closeTo(expectedDurationInMs, marginOfErrorInMs))); } } From 43a4d08c363242fd565f2f5cc3c03456b8a1b95d Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Wed, 31 Jan 2018 18:50:37 +0100 Subject: [PATCH 083/118] Sudoku refactor (#3556) * BacktrackingAlgorithm refactor * DancingLinks refactor --- .../sudoku/BacktrackingAlgorithm.java | 79 ++++++++++--------- .../algorithms/sudoku/ColumnNode.java | 2 +- .../algorithms/sudoku/DancingLinks.java | 23 +++--- .../sudoku/DancingLinksAlgorithm.java | 46 ++++++----- .../algorithms/sudoku/DancingNode.java | 4 +- 5 files changed, 76 insertions(+), 78 deletions(-) diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java index 127e78900c..dc2a324c12 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java @@ -3,25 +3,25 @@ package com.baeldung.algorithms.sudoku; import java.util.stream.IntStream; public class BacktrackingAlgorithm { - - private static int BOARD_SIZE = 9; - private static int SUBSECTION_SIZE = 3; - private static int BOARD_START_INDEX = 0; - - private static int NO_VALUE = 0; - private static int MIN_VALUE = 1; - private static int MAX_VALUE = 9; - public static int[][] board = { - { 8, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 3, 6, 0, 0, 0, 0, 0 }, - { 0, 7, 0, 0, 9, 0, 2, 0, 0 }, - { 0, 5, 0, 0, 0, 7, 0, 0, 0 }, - { 0, 0, 0, 0, 4, 5, 7, 0, 0 }, - { 0, 0, 0, 1, 0, 0, 0, 3, 0 }, - { 0, 0, 1, 0, 0, 0, 0, 6, 8 }, - { 0, 0, 8, 5, 0, 0, 0, 1, 0 }, - { 0, 9, 0, 0, 0, 0, 4, 0, 0 } + private static final int BOARD_SIZE = 9; + private static final int SUBSECTION_SIZE = 3; + private static final int BOARD_START_INDEX = 0; + + private static final int NO_VALUE = 0; + private static final int MIN_VALUE = 1; + private static final int MAX_VALUE = 9; + + private static int[][] board = { + {8, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 3, 6, 0, 0, 0, 0, 0}, + {0, 7, 0, 0, 9, 0, 2, 0, 0}, + {0, 5, 0, 0, 0, 7, 0, 0, 0}, + {0, 0, 0, 0, 4, 5, 7, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 3, 0}, + {0, 0, 1, 0, 0, 0, 0, 6, 8}, + {0, 0, 8, 5, 0, 0, 0, 1, 0}, + {0, 9, 0, 0, 0, 0, 4, 0, 0} }; public static void main(String[] args) { @@ -30,7 +30,7 @@ public class BacktrackingAlgorithm { solver.printBoard(); } - public void printBoard() { + private void printBoard() { for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) { for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) { System.out.print(board[row][column] + " "); @@ -39,7 +39,7 @@ public class BacktrackingAlgorithm { } } - public boolean solve(int[][] board) { + private boolean solve(int[][] board) { for (int r = BOARD_START_INDEX; r < BOARD_SIZE; r++) { for (int c = BOARD_START_INDEX; c < BOARD_SIZE; c++) { if (board[r][c] == NO_VALUE) { @@ -47,9 +47,8 @@ public class BacktrackingAlgorithm { board[r][c] = k; if (isValid(board, r, c) && solve(board)) { return true; - } else { - board[r][c] = NO_VALUE; } + board[r][c] = NO_VALUE; } return false; } @@ -58,16 +57,22 @@ public class BacktrackingAlgorithm { return true; } - public boolean isValid(int[][] board, int r, int c) { - return (rowConstraint(board, r) && - columnConstraint(board, c) && - subsectionConstraint(board, r, c)); + private boolean isValid(int[][] board, int r, int c) { + return rowConstraint(board, r) && + columnConstraint(board, c) && + subsectionConstraint(board, r, c); } private boolean subsectionConstraint(int[][] board, int r, int c) { boolean[] constraint = new boolean[BOARD_SIZE]; - for (int i = (r / SUBSECTION_SIZE) * SUBSECTION_SIZE; i < (r / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; i++) { - for (int j = (c / SUBSECTION_SIZE) * SUBSECTION_SIZE; j < (c / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; j++) { + int subsectionRowStart = (r / SUBSECTION_SIZE) * SUBSECTION_SIZE; + int subsectionRowEnd = subsectionRowStart + SUBSECTION_SIZE; + + int subsectionColumnStart = (c / SUBSECTION_SIZE) * SUBSECTION_SIZE; + int subsectionColumnEnd = subsectionColumnStart + SUBSECTION_SIZE; + + for (int i = subsectionRowStart; i < subsectionRowEnd; i++) { + for (int j = subsectionColumnStart; j < subsectionColumnEnd; j++) { if (!checkConstraint(board, i, constraint, j)) return false; } } @@ -76,23 +81,19 @@ public class BacktrackingAlgorithm { private boolean columnConstraint(int[][] board, int c) { boolean[] constraint = new boolean[BOARD_SIZE]; - for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) { - if (!checkConstraint(board, i, constraint, c)) return false; - } - return true; + return IntStream.range(BOARD_START_INDEX, BOARD_SIZE) + .allMatch(i -> checkConstraint(board, i, constraint, c)); } private boolean rowConstraint(int[][] board, int r) { boolean[] constraint = new boolean[BOARD_SIZE]; - for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) { - if (!checkConstraint(board, r, constraint, i)) return false; - } - return true; + return IntStream.range(BOARD_START_INDEX, BOARD_SIZE) + .allMatch(i -> checkConstraint(board, r, constraint, i)); } private boolean checkConstraint(int[][] board, int r, boolean[] constraint, int c) { - if (board[r][c] >= MIN_VALUE && board[r][c] <= MAX_VALUE) { - if (constraint[board[r][c] - 1] == false) { + if (board[r][c] != NO_VALUE) { + if (!constraint[board[r][c] - 1]) { constraint[board[r][c] - 1] = true; } else { return false; @@ -100,4 +101,4 @@ public class BacktrackingAlgorithm { } return true; } -} +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java index 48538344b6..46995ca42f 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java @@ -4,7 +4,7 @@ class ColumnNode extends DancingNode { int size; String name; - public ColumnNode(String n) { + ColumnNode(String n) { super(); size = 0; name = n; diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java index a30f8ecab5..e5a02b7c91 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java @@ -50,10 +50,9 @@ public class DancingLinks { private ColumnNode makeDLXBoard(boolean[][] grid) { final int COLS = grid[0].length; - final int ROWS = grid.length; ColumnNode headerNode = new ColumnNode("header"); - ArrayList columnNodes = new ArrayList(); + List columnNodes = new ArrayList<>(); for (int i = 0; i < COLS; i++) { ColumnNode n = new ColumnNode(Integer.toString(i)); @@ -62,10 +61,10 @@ public class DancingLinks { } headerNode = headerNode.R.C; - for (int i = 0; i < ROWS; i++) { + for (boolean[] aGrid : grid) { DancingNode prev = null; for (int j = 0; j < COLS; j++) { - if (grid[i][j] == true) { + if (aGrid[j]) { ColumnNode col = columnNodes.get(j); DancingNode newNode = new DancingNode(col); if (prev == null) @@ -82,21 +81,21 @@ public class DancingLinks { return headerNode; } - public DancingLinks(boolean[][] cover) { + DancingLinks(boolean[][] cover) { header = makeDLXBoard(cover); } public void runSolver() { - answer = new LinkedList(); + answer = new LinkedList<>(); search(0); } - public void handleSolution(List answer) { + private void handleSolution(List answer) { int[][] result = parseBoard(answer); printSolution(result); } - int size = 9; + private int size = 9; private int[][] parseBoard(List answer) { int[][] result = new int[size][size]; @@ -120,12 +119,12 @@ public class DancingLinks { return result; } - public static void printSolution(int[][] result) { + private static void printSolution(int[][] result) { int N = result.length; - for (int i = 0; i < N; i++) { - String ret = ""; + for (int[] aResult : result) { + StringBuilder ret = new StringBuilder(); for (int j = 0; j < N; j++) { - ret += result[i][j] + " "; + ret.append(aResult[j]).append(" "); } System.out.println(ret); } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java index 057a15c594..6b0f57a075 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java @@ -1,39 +1,37 @@ package com.baeldung.algorithms.sudoku; -import java.util.*; +import java.util.Arrays; public class DancingLinksAlgorithm { - private static int BOARD_SIZE = 9; - private static int SUBSECTION_SIZE = 3; - private static int NO_VALUE = 0; - private static int CONSTRAINTS = 4; - private static int MIN_VALUE = 1; - private static int MAX_VALUE = 9; - private static int COVER_START_INDEX = 1; + private static final int BOARD_SIZE = 9; + private static final int SUBSECTION_SIZE = 3; + private static final int NO_VALUE = 0; + private static final int CONSTRAINTS = 4; + private static final int MIN_VALUE = 1; + private static final int MAX_VALUE = 9; + private static final int COVER_START_INDEX = 1; - public static int[][] board = { - { 8, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 3, 6, 0, 0, 0, 0, 0 }, - { 0, 7, 0, 0, 9, 0, 2, 0, 0 }, - { 0, 5, 0, 0, 0, 7, 0, 0, 0 }, - { 0, 0, 0, 0, 4, 5, 7, 0, 0 }, - { 0, 0, 0, 1, 0, 0, 0, 3, 0 }, - { 0, 0, 1, 0, 0, 0, 0, 6, 8 }, - { 0, 0, 8, 5, 0, 0, 0, 1, 0 }, - { 0, 9, 0, 0, 0, 0, 4, 0, 0 } - }; + private static int[][] board = { + {8, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 3, 6, 0, 0, 0, 0, 0}, + {0, 7, 0, 0, 9, 0, 2, 0, 0}, + {0, 5, 0, 0, 0, 7, 0, 0, 0}, + {0, 0, 0, 0, 4, 5, 7, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 3, 0}, + {0, 0, 1, 0, 0, 0, 0, 6, 8}, + {0, 0, 8, 5, 0, 0, 0, 1, 0}, + {0, 9, 0, 0, 0, 0, 4, 0, 0} + }; public static void main(String[] args) { DancingLinksAlgorithm solver = new DancingLinksAlgorithm(); solver.solve(board); } - public boolean solve(int[][] board) { + private void solve(int[][] board) { boolean[][] cover = initializeExactCoverBoard(board); DancingLinks dlx = new DancingLinks(cover); dlx.runSolver(); - - return true; } private int getIndex(int row, int col, int num) { @@ -54,7 +52,7 @@ public class DancingLinksAlgorithm { } } } - + // Row constrain. for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { @@ -74,7 +72,7 @@ public class DancingLinksAlgorithm { } } } - + // Subsection constraint for (int br = COVER_START_INDEX; br <= BOARD_SIZE; br += SUBSECTION_SIZE) { for (int bc = COVER_START_INDEX; bc <= BOARD_SIZE; bc += SUBSECTION_SIZE) { diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java index 13dc3f2b57..b494eba9ef 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java @@ -39,11 +39,11 @@ class DancingNode { this.U.D = this.D.U = this; } - public DancingNode() { + DancingNode() { L = R = U = D = this; } - public DancingNode(ColumnNode c) { + DancingNode(ColumnNode c) { this(); C = c; } From 6e23e8f4fdc8bf1b1e37570c6d69fad80cef781d Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Thu, 1 Feb 2018 03:16:53 +0700 Subject: [PATCH 084/118] Initial commit for BAEL-1516 --- .../com/baeldung/testing/assertj/Member.java | 19 +++++ .../assertj/AssertJConditionUnitTest.java | 72 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java new file mode 100644 index 0000000000..a0b3d0daac --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java @@ -0,0 +1,19 @@ +package com.baeldung.testing.assertj; + +public class Member { + private String name; + private int age; + + public Member(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java new file mode 100644 index 0000000000..153af828f1 --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java @@ -0,0 +1,72 @@ +package com.baeldung.testing.assertj; + +import static org.assertj.core.api.Assertions.allOf; +import static org.assertj.core.api.Assertions.anyOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.not; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.assertj.core.api.Condition; +import org.junit.Test; + +public class AssertJConditionUnitTest { + private Condition senior = new Condition<>(m -> m.getAge() >= 60, "senior"); + private Condition nameJohn = new Condition<>(m -> m.getName().equalsIgnoreCase("John"), "name John"); + + @Test + public void whenUsingMemberAgeCondition_thenCorrect() { + Member member = new Member("John", 65); + assertThat(member).is(senior); + + try { + assertThat(member).isNot(senior); + fail(); + } catch (AssertionError e) { + assertThat(e).hasMessageContaining("not to be "); + } + } + + @Test + public void whenUsingMemberNameCondition_thenCorrect() { + Member member = new Member("Jane", 60); + assertThat(member).doesNotHave(nameJohn); + + try { + assertThat(member).has(nameJohn); + fail(); + } catch (AssertionError e) { + assertThat(e).hasMessageContaining("to have:\n "); + } + } + + @Test + public void whenCollectionConditionsAreSatisfied_thenCorrect() { + List members = new ArrayList<>(); + members.add(new Member("Alice", 50)); + members.add(new Member("Bob", 60)); + + assertThat(members).haveExactly(1, senior); + assertThat(members).doNotHave(nameJohn); + } + + @Test + public void whenCombiningAllOfConditions_thenCorrect() { + Member john = new Member("John", 60); + Member jane = new Member("Jane", 50); + + assertThat(john).is(allOf(senior, nameJohn)); + assertThat(jane).is(allOf(not(nameJohn), not(senior))); + } + + @Test + public void whenCombiningAnyOfConditions_thenCorrect() { + Member john = new Member("John", 50); + Member jane = new Member("Jane", 60); + + assertThat(john).is(anyOf(senior, nameJohn)); + assertThat(jane).is(anyOf(nameJohn, senior)); + } +} From 3e4b6df194059c0fc95c22e7fe2bcf07ea6c1224 Mon Sep 17 00:00:00 2001 From: ramansahasi Date: Thu, 1 Feb 2018 02:36:16 +0530 Subject: [PATCH 085/118] Interrupted thread before logging (#3563) --- .../main/java/com/baeldung/concurrent/waitandnotify/Data.java | 4 ++-- .../java/com/baeldung/concurrent/waitandnotify/Receiver.java | 1 + .../java/com/baeldung/concurrent/waitandnotify/Sender.java | 1 + .../concurrent/waitandnotify/NetworkIntegrationTest.java | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) 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 d9e7434e0c..9d76c1fcd1 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,7 +1,5 @@ package com.baeldung.concurrent.waitandnotify; -import org.slf4j.Logger; - public class Data { private String packet; @@ -14,6 +12,7 @@ public class Data { try { wait(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); System.out.println("Thread Interrupted"); } } @@ -28,6 +27,7 @@ public class Data { try { wait(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); System.out.println("Thread Interrupted"); } } 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 724e908a99..21ba822bfd 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 @@ -20,6 +20,7 @@ public class Receiver implements Runnable { try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); System.out.println("Thread Interrupted"); } } 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 b4945b7b73..c365294cdd 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 @@ -25,6 +25,7 @@ public class Sender implements Runnable { try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); System.out.println("Thread Interrupted"); } } 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 8ecc92236e..e2bc328df3 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 @@ -57,7 +57,8 @@ public class NetworkIntegrationTest { sender.join(); receiver.join(); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); } assertEquals(expected, outContent.toString()); From 63b8aa334f89ab042e4203f99d032b03a1dd8b60 Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Thu, 1 Feb 2018 11:00:51 +0700 Subject: [PATCH 086/118] Refactor BAEL-1515 --- testing-modules/testing/pom.xml | 1 - .../custom/AssertJCarAssertUnitTest.java | 20 ----------- .../AssertJCustomAssertionsUnitTest.java | 35 +++++++++++++------ .../custom/AssertJPersonAssertUnitTest.java | 26 -------------- .../testing/assertj/custom/Assertions.java | 4 +-- .../testing/assertj/custom/CarAssert.java | 30 ---------------- 6 files changed, 26 insertions(+), 90 deletions(-) delete mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java delete mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java delete mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 6f185d3b4c..c76045380b 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -161,7 +161,6 @@ com.baeldung.testing.assertj.custom.Person - com.baeldung.testing.assertj.custom.Car diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java deleted file mode 100644 index d438cc42f6..0000000000 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCarAssertUnitTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.testing.assertj.custom; - -import static com.baeldung.testing.assertj.custom.CarAssert.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -public class AssertJCarAssertUnitTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void whenCarTypeDoesNotMatch_thenIncorrect() { - thrown.expect(AssertionError.class); - thrown.expectMessage("Expected type SUV but was Sedan"); - Car car = new Car("Sedan"); - assertThat(car).hasType("SUV"); - } -} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java index 8b800de3db..f9b5bad039 100644 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java @@ -1,6 +1,7 @@ package com.baeldung.testing.assertj.custom; import static com.baeldung.testing.assertj.custom.Assertions.assertThat; +import static org.junit.Assert.fail; import org.junit.Rule; import org.junit.Test; @@ -9,21 +10,35 @@ import org.junit.rules.ExpectedException; public class AssertJCustomAssertionsUnitTest { @Rule public ExpectedException thrown = ExpectedException.none(); - + @Test - public void whenPersonDoesNotHaveAMatchingNickname_thenIncorrect() { - thrown.expect(AssertionError.class); - thrown.expectMessage("Expected nickname John but did not have"); + public void whenPersonNameMatches_thenCorrect() { Person person = new Person("John Doe", 20); - person.addNickname("Nick"); - assertThat(person).hasNickname("John"); + assertThat(person).hasFullName("John Doe"); } @Test - public void whenCarIsUsed_thenCorrect() { + public void whenPersonAgeLessThanEighteen_thenNotAdult() { Person person = new Person("Jane Roe", 16); - Car car = new Car("SUV"); - car.setOwner(person); - assertThat(car).isUsed(); + + try { + assertThat(person).isAdult(); + fail(); + } catch (AssertionError e) { + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected adult but was juvenile"); + } + } + + @Test + public void whenPersonDoesNotHaveAMatchingNickname_thenIncorrect() { + Person person = new Person("John Doe", 20); + person.addNickname("Nick"); + + try { + assertThat(person).hasNickname("John"); + fail(); + } catch (AssertionError e) { + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected nickname John but did not have"); + } } } diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java deleted file mode 100644 index ab421915af..0000000000 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJPersonAssertUnitTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.baeldung.testing.assertj.custom; - -import static com.baeldung.testing.assertj.custom.PersonAssert.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -public class AssertJPersonAssertUnitTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void whenPersonNameMatches_thenCorrect() { - Person person = new Person("John Doe", 20); - assertThat(person).hasFullName("John Doe"); - } - - @Test - public void whenPersonAgeLessThanEighteen_thenNotAdult() { - thrown.expect(AssertionError.class); - thrown.expectMessage("Expected adult but was juvenile"); - Person person = new Person("Jane Roe", 16); - assertThat(person).isAdult(); - } -} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java index 5c72eb6d05..fcffb8fc6c 100644 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java @@ -5,7 +5,5 @@ public class Assertions { return new PersonAssert(actual); } - public static CarAssert assertThat(Car actual) { - return new CarAssert(actual); - } + // static factory methods of other assertion classes } diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java deleted file mode 100644 index 413c2d3e12..0000000000 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/CarAssert.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.baeldung.testing.assertj.custom; - -import org.assertj.core.api.AbstractAssert; - -public class CarAssert extends AbstractAssert { - - public CarAssert(Car actual) { - super(actual, CarAssert.class); - } - - public static CarAssert assertThat(Car actual) { - return new CarAssert(actual); - } - - public CarAssert hasType(String type) { - isNotNull(); - if (!actual.getType().equals(type)) { - failWithMessage("Expected type %s but was %s", type, actual.getType()); - } - return this; - } - - public CarAssert isUsed() { - isNotNull(); - if (actual.getOwner() == null) { - failWithMessage("Expected old but was new"); - } - return this; - } -} From 15f18bbb83a7472328225851f527db78f938c771 Mon Sep 17 00:00:00 2001 From: ramansahasi Date: Thu, 1 Feb 2018 15:27:48 +0530 Subject: [PATCH 087/118] BAEL 1269 Intro to JSON-JAVA (#3493) * Final commit * Made changes as per last review * Moved from core-java to json module --- json/pom.xml | 5 ++ .../java/com/baeldung/jsonjava/CDLDemo.java | 59 +++++++++++++++++++ .../com/baeldung/jsonjava/CookieDemo.java | 30 ++++++++++ .../java/com/baeldung/jsonjava/DemoBean.java | 26 ++++++++ .../java/com/baeldung/jsonjava/HTTPDemo.java | 27 +++++++++ .../com/baeldung/jsonjava/JSONArrayDemo.java | 52 ++++++++++++++++ .../com/baeldung/jsonjava/JSONObjectDemo.java | 59 +++++++++++++++++++ .../baeldung/jsonjava/JSONTokenerDemo.java | 13 ++++ .../baeldung/jsonjava/CDLIntegrationTest.java | 52 ++++++++++++++++ .../jsonjava/CookieIntegrationTest.java | 29 +++++++++ .../jsonjava/HTTPIntegrationTest.java | 25 ++++++++ .../jsonjava/JSONArrayIntegrationTest.java | 47 +++++++++++++++ .../jsonjava/JSONObjectIntegrationTest.java | 53 +++++++++++++++++ .../jsonjava/JSONTokenerIntegrationTest.java | 21 +++++++ 14 files changed, 498 insertions(+) create mode 100644 json/src/main/java/com/baeldung/jsonjava/CDLDemo.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/CookieDemo.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/DemoBean.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java create mode 100644 json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java create mode 100644 json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java diff --git a/json/pom.xml b/json/pom.xml index 958dd32f53..7c74d425af 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -31,6 +31,11 @@ ${fastjson.version} + + org.json + json + 20171018 + diff --git a/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java b/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java new file mode 100644 index 0000000000..f5fee0c4a9 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java @@ -0,0 +1,59 @@ +package com.baeldung.jsonjava; + +import org.json.CDL; +import org.json.JSONArray; +import org.json.JSONTokener; + +public class CDLDemo { + public static void main(String[] args) { + System.out.println("7.1. Producing JSONArray Directly from Comma Delimited Text: "); + jsonArrayFromCDT(); + + System.out.println("\n7.2. Producing Comma Delimited Text from JSONArray: "); + cDTfromJSONArray(); + + System.out.println("\n7.3.1. Producing JSONArray of JSONObjects Using Comma Delimited Text: "); + jaOfJOFromCDT2(); + + System.out.println("\n7.3.2. Producing JSONArray of JSONObjects Using Comma Delimited Text: "); + jaOfJOFromCDT2(); + } + + public static void jsonArrayFromCDT() { + JSONArray ja = CDL.rowToJSONArray(new JSONTokener("England, USA, Canada")); + System.out.println(ja); + } + + public static void cDTfromJSONArray() { + JSONArray ja = new JSONArray("[\"England\",\"USA\",\"Canada\"]"); + String cdt = CDL.rowToString(ja); + System.out.println(cdt); + } + + public static void jaOfJOFromCDT() { + String string = + "name, city, age \n" + + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(string); + System.out.println(result.toString()); + } + + public static void jaOfJOFromCDT2() { + JSONArray ja = new JSONArray(); + ja.put("name"); + ja.put("city"); + ja.put("age"); + + String string = + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(ja, string); + System.out.println(result.toString()); + } + +} diff --git a/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java b/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java new file mode 100644 index 0000000000..bc39d13642 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java @@ -0,0 +1,30 @@ +package com.baeldung.jsonjava; + +import org.json.Cookie; +import org.json.JSONObject; + +public class CookieDemo { + public static void main(String[] args) { + System.out.println("8.1. Converting a Cookie String into a JSONObject"); + cookieStringToJSONObject(); + + System.out.println("\n8.2. Converting a JSONObject into Cookie String"); + jSONObjectToCookieString(); + } + + public static void cookieStringToJSONObject() { + String cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"; + JSONObject cookieJO = Cookie.toJSONObject(cookie); + System.out.println(cookieJO); + } + + public static void jSONObjectToCookieString() { + JSONObject cookieJO = new JSONObject(); + cookieJO.put("name", "username"); + cookieJO.put("value", "John Doe"); + cookieJO.put("expires", "Thu, 18 Dec 2013 12:00:00 UTC"); + cookieJO.put("path", "/"); + String cookie = Cookie.toString(cookieJO); + System.out.println(cookie); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/DemoBean.java b/json/src/main/java/com/baeldung/jsonjava/DemoBean.java new file mode 100644 index 0000000000..6d27b329c2 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/DemoBean.java @@ -0,0 +1,26 @@ +package com.baeldung.jsonjava; + +public class DemoBean { + private int id; + private String name; + private boolean active; + + public int getId() { + return id; + } + public void setId(int id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public boolean isActive() { + return active; + } + public void setActive(boolean active) { + this.active = active; + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java b/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java new file mode 100644 index 0000000000..800018005c --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java @@ -0,0 +1,27 @@ +package com.baeldung.jsonjava; + +import org.json.HTTP; +import org.json.JSONObject; + +public class HTTPDemo { + public static void main(String[] args) { + System.out.println("9.1. Converting JSONObject to HTTP Header: "); + jSONObjectToHTTPHeader(); + + System.out.println("\n9.2. Converting HTTP Header String Back to JSONObject: "); + hTTPHeaderToJSONObject(); + } + + public static void jSONObjectToHTTPHeader() { + JSONObject jo = new JSONObject(); + jo.put("Method", "POST"); + jo.put("Request-URI", "http://www.example.com/"); + jo.put("HTTP-Version", "HTTP/1.1"); + System.out.println(HTTP.toString(jo)); + } + + public static void hTTPHeaderToJSONObject() { + JSONObject obj = HTTP.toJSONObject("POST \"http://www.example.com/\" HTTP/1.1"); + System.out.println(obj); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java new file mode 100644 index 0000000000..2a4fab2eab --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java @@ -0,0 +1,52 @@ +package com.baeldung.jsonjava; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class JSONArrayDemo { + public static void main(String[] args) { + System.out.println("5.1. Creating JSON Array: "); + creatingJSONArray(); + + System.out.println("\n5.2. Creating JSON Array from JSON string: "); + jsonArrayFromJSONString(); + + System.out.println("\n5.3. Creating JSON Array from Collection Object: "); + jsonArrayFromCollectionObj(); + } + + public static void creatingJSONArray() { + JSONArray ja = new JSONArray(); + ja.put(Boolean.TRUE); + ja.put("lorem ipsum"); + + // We can also put a JSONObject in JSONArray + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + ja.put(jo); + + System.out.println(ja.toString()); + } + + public static void jsonArrayFromJSONString() { + JSONArray ja = new JSONArray("[true, \"lorem ipsum\", 215]"); + System.out.println(ja); + } + + public static void jsonArrayFromCollectionObj() { + List list = new ArrayList<>(); + list.add("California"); + list.add("Texas"); + list.add("Hawaii"); + list.add("Alaska"); + + JSONArray ja = new JSONArray(list); + System.out.println(ja); + } +} \ No newline at end of file diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java new file mode 100644 index 0000000000..cfe8467c30 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java @@ -0,0 +1,59 @@ +package com.baeldung.jsonjava; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; + +public class JSONObjectDemo { + public static void main(String[] args) { + System.out.println("4.1. Creating JSONObject: "); + jsonFromJSONObject(); + + System.out.println("\n4.2. Creating JSONObject from Map: "); + jsonFromMap(); + + System.out.println("\n4.3. Creating JSONObject from JSON string: "); + jsonFromJSONString(); + + System.out.println("\n4.4. Creating JSONObject from Java Bean: "); + jsonFromDemoBean(); + } + + public static void jsonFromJSONObject() { + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + System.out.println(jo.toString()); + } + + public static void jsonFromMap() { + Map map = new HashMap<>(); + map.put("name", "jon doe"); + map.put("age", "22"); + map.put("city", "chicago"); + JSONObject jo = new JSONObject(map); + + System.out.println(jo.toString()); + } + + public static void jsonFromJSONString() { + JSONObject jo = new JSONObject( + "{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}" + ); + + System.out.println(jo.toString()); + } + + public static void jsonFromDemoBean() { + DemoBean demo = new DemoBean(); + demo.setId(1); + demo.setName("lorem ipsum"); + demo.setActive(true); + + JSONObject jo = new JSONObject(demo); + System.out.println(jo); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java new file mode 100644 index 0000000000..fb9fea959c --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java @@ -0,0 +1,13 @@ +package com.baeldung.jsonjava; + +import org.json.JSONTokener; + +public class JSONTokenerDemo { + public static void main(String[] args) { + JSONTokener jt = new JSONTokener("Sample String"); + + while(jt.more()) { + System.out.println(jt.next()); + } + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java new file mode 100644 index 0000000000..441c71e78e --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.CDL; +import org.json.JSONArray; +import org.json.JSONTokener; +import org.junit.Test; + +public class CDLIntegrationTest { + @Test + public void givenCommaDelimitedText_thenConvertToJSONArray() { + JSONArray ja = CDL.rowToJSONArray(new JSONTokener("England, USA, Canada")); + assertEquals("[\"England\",\"USA\",\"Canada\"]", ja.toString()); + } + + @Test + public void givenJSONArray_thenConvertToCommaDelimitedText() { + JSONArray ja = new JSONArray("[\"England\",\"USA\",\"Canada\"]"); + String cdt = CDL.rowToString(ja); + assertEquals("England,USA,Canada", cdt.toString().trim()); + } + + @Test + public void givenCommaDelimitedText_thenGetJSONArrayOfJSONObjects() { + String string = + "name, city, age \n" + + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(string); + assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + } + + @Test + public void givenCommaDelimitedText_thenGetJSONArrayOfJSONObjects2() { + JSONArray ja = new JSONArray(); + ja.put("name"); + ja.put("city"); + ja.put("age"); + + String string = + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(ja, string); + assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + } + +} diff --git a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java new file mode 100644 index 0000000000..c1a3505bbc --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java @@ -0,0 +1,29 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.Cookie; +import org.json.JSONObject; +import org.junit.Test; + +public class CookieIntegrationTest { + @Test + public void givenCookieString_thenConvertToJSONObject() { + String cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"; + JSONObject cookieJO = Cookie.toJSONObject(cookie); + + assertEquals("{\"path\":\"/\",\"expires\":\"Thu, 18 Dec 2013 12:00:00 UTC\",\"name\":\"username\",\"value\":\"John Doe\"}", cookieJO.toString()); + } + + @Test + public void givenJSONObject_thenConvertToCookieString() { + JSONObject cookieJO = new JSONObject(); + cookieJO.put("name", "username"); + cookieJO.put("value", "John Doe"); + cookieJO.put("expires", "Thu, 18 Dec 2013 12:00:00 UTC"); + cookieJO.put("path", "/"); + String cookie = Cookie.toString(cookieJO); + + assertEquals("username=John Doe;expires=Thu, 18 Dec 2013 12:00:00 UTC;path=/", cookie.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java new file mode 100644 index 0000000000..1aa0427c7e --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java @@ -0,0 +1,25 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; +import org.json.HTTP; +import org.json.JSONObject; +import org.junit.Test; + +public class HTTPIntegrationTest { + @Test + public void givenJSONObject_thenConvertToHTTPHeader() { + JSONObject jo = new JSONObject(); + jo.put("Method", "POST"); + jo.put("Request-URI", "http://www.example.com/"); + jo.put("HTTP-Version", "HTTP/1.1"); + + assertEquals("POST \"http://www.example.com/\" HTTP/1.1"+HTTP.CRLF+HTTP.CRLF, HTTP.toString(jo)); + } + + @Test + public void givenHTTPHeader_thenConvertToJSONObject() { + JSONObject obj = HTTP.toJSONObject("POST \"http://www.example.com/\" HTTP/1.1"); + + assertEquals("{\"Request-URI\":\"http://www.example.com/\",\"Method\":\"POST\",\"HTTP-Version\":\"HTTP/1.1\"}", obj.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java new file mode 100644 index 0000000000..c956232abe --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java @@ -0,0 +1,47 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; + +public class JSONArrayIntegrationTest { + @Test + public void givenJSONJava_thenCreateNewJSONArrayFromScratch() { + JSONArray ja = new JSONArray(); + ja.put(Boolean.TRUE); + ja.put("lorem ipsum"); + + // We can also put a JSONObject in JSONArray + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + ja.put(jo); + + assertEquals("[true,\"lorem ipsum\",{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}]", ja.toString()); + } + + @Test + public void givenJsonString_thenCreateNewJSONArray() { + JSONArray ja = new JSONArray("[true, \"lorem ipsum\", 215]"); + assertEquals("[true,\"lorem ipsum\",215]", ja.toString()); + } + + @Test + public void givenListObject_thenConvertItToJSONArray() { + List list = new ArrayList<>(); + list.add("California"); + list.add("Texas"); + list.add("Hawaii"); + list.add("Alaska"); + + JSONArray ja = new JSONArray(list); + assertEquals("[\"California\",\"Texas\",\"Hawaii\",\"Alaska\"]", ja.toString()); + } +} \ No newline at end of file diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java new file mode 100644 index 0000000000..70f7921797 --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java @@ -0,0 +1,53 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; +import org.junit.Test; + +public class JSONObjectIntegrationTest { + @Test + public void givenJSONJava_thenCreateNewJSONObject() { + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); + + } + + @Test + public void givenMapObject_thenCreateJSONObject() { + Map map = new HashMap<>(); + map.put("name", "jon doe"); + map.put("age", "22"); + map.put("city", "chicago"); + JSONObject jo = new JSONObject(map); + + assertEquals("{\"name\":\"jon doe\",\"city\":\"chicago\",\"age\":\"22\"}", jo.toString()); + } + + @Test + public void givenJsonString_thenCreateJSONObject() { + JSONObject jo = new JSONObject( + "{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}" + ); + + assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); + } + + @Test + public void givenDemoBean_thenCreateJSONObject() { + DemoBean demo = new DemoBean(); + demo.setId(1); + demo.setName("lorem ipsum"); + demo.setActive(true); + + JSONObject jo = new JSONObject(demo); + assertEquals("{\"name\":\"lorem ipsum\",\"active\":true,\"id\":1}", jo.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java new file mode 100644 index 0000000000..4fe8f27231 --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java @@ -0,0 +1,21 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.JSONTokener; +import org.junit.Test; + +public class JSONTokenerIntegrationTest { + @Test + public void givenString_convertItToJSONTokens() { + String str = "Sample String"; + JSONTokener jt = new JSONTokener(str); + + char[] expectedTokens = str.toCharArray(); + int index = 0; + + while(jt.more()) { + assertEquals(expectedTokens[index++], jt.next()); + } + } +} From 72c9fea7e23c479468464b9ff7e4470d11aaf6eb Mon Sep 17 00:00:00 2001 From: tamasradu Date: Thu, 1 Feb 2018 15:56:36 +0200 Subject: [PATCH 088/118] Radutamas/bael 1487 (#3569) * Code for test article: Different Types of Bean Injection in Spring * Adding jUnits for test article: Different Types of Bean Injection in Spring * BAEL-1265: Adding jUnit for article * BAEL-1265: Closing ExecutorService in jUnit * BAEL-1487: Adding test for AsyncHtpClient tutorial --- libraries/pom.xml | 9 +- .../AsyncHttpClientTestCase.java | 219 ++++++++++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java diff --git a/libraries/pom.xml b/libraries/pom.xml index a330494bb3..e8f7c470a3 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -116,6 +116,12 @@ + + + org.asynchttpclient + async-http-client + ${async.http.client.version} + org.beykery @@ -766,7 +772,7 @@ 1.1.3-rc.5 1.4.0 1.1.0 - 4.1.15.Final + 4.1.20.Final 4.1 4.12 0.10 @@ -796,5 +802,6 @@ 1.0.0 1.7.0 3.0.14 + 2.2.0 \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java new file mode 100644 index 0000000000..7f9c2699fe --- /dev/null +++ b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java @@ -0,0 +1,219 @@ +package com.baeldung.asynchttpclient; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.asynchttpclient.AsyncCompletionHandler; +import org.asynchttpclient.AsyncHandler; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.Dsl; +import org.asynchttpclient.HttpResponseBodyPart; +import org.asynchttpclient.HttpResponseStatus; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Request; +import org.asynchttpclient.Response; +import org.asynchttpclient.ws.WebSocket; +import org.asynchttpclient.ws.WebSocketListener; +import org.asynchttpclient.ws.WebSocketUpgradeHandler; +import org.junit.Before; +import org.junit.Test; + +import io.netty.handler.codec.http.HttpHeaders; + +public class AsyncHttpClientTestCase { + + private static AsyncHttpClient HTTP_CLIENT; + + @Before + public void setup() { + + AsyncHttpClientConfig clientConfig = Dsl.config().setConnectTimeout(15000).setRequestTimeout(15000).build(); + HTTP_CLIENT = Dsl.asyncHttpClient(clientConfig); + } + + @Test + public void givenHttpClient_executeSyncGetRequest() { + + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + + Future responseFuture = boundGetRequest.execute(); + try { + Response response = responseFuture.get(5000, TimeUnit.MILLISECONDS); + assertNotNull(response); + assertEquals(200, response.getStatusCode()); + + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequest() { + + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) throws Exception { + + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); + + // execute a bound GET request + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + + boundGetRequest.execute(new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) throws Exception { + + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequestWithAsyncHandler() { + + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncHandler() { + + int responseStatusCode = -1; + + @Override + public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + responseStatusCode = responseStatus.getStatusCode(); + return State.CONTINUE; + } + + @Override + public State onHeadersReceived(HttpHeaders headers) throws Exception { + return State.CONTINUE; + } + + @Override + public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + return State.CONTINUE; + } + + @Override + public void onThrowable(Throwable t) { + + } + + @Override + public Integer onCompleted() throws Exception { + assertEquals(200, responseStatusCode); + return responseStatusCode; + } + }); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequestWithListanableFuture() { + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + ListenableFuture listenableFuture = HTTP_CLIENT.executeRequest(unboundGetRequest); + listenableFuture.addListener(() -> { + Response response; + try { + response = listenableFuture.get(5000, TimeUnit.MILLISECONDS); + assertEquals(200, response.getStatusCode()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + + }, Executors.newCachedThreadPool()); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenWebSocketClient_tryToConnect() { + + WebSocketUpgradeHandler.Builder upgradeHandlerBuilder = new WebSocketUpgradeHandler.Builder(); + WebSocketUpgradeHandler wsHandler = upgradeHandlerBuilder.addWebSocketListener(new WebSocketListener() { + @Override + public void onOpen(WebSocket websocket) { + // WebSocket connection opened + } + + @Override + public void onClose(WebSocket websocket, int code, String reason) { + // WebSocket connection closed + } + + @Override + public void onError(Throwable t) { + // WebSocket connection error + assertTrue(t.getMessage().contains("Request timeout")); + } + }).build(); + + WebSocket WEBSOCKET_CLIENT = null; + try { + WEBSOCKET_CLIENT = Dsl.asyncHttpClient() + .prepareGet("ws://localhost:5590/websocket") + .addHeader("header_name", "header_value") + .addQueryParam("key", "value") + .setRequestTimeout(5000) + .execute(wsHandler).get(); + + if (WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendPingFrame(); + WEBSOCKET_CLIENT.sendTextFrame("test message"); + WEBSOCKET_CLIENT.sendBinaryFrame(new byte[] { 't', 'e', 's', 't' }); + } + + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } finally { + if (WEBSOCKET_CLIENT != null && WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendCloseFrame(200, "OK"); + } + } + } +} From 310e31e2bd5c1ca5a4f021268f09e6b0808807e5 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 1 Feb 2018 20:30:54 +0100 Subject: [PATCH 089/118] Code for BAEL-68 (#3559) --- .../HibernateBootstrapIntegrationTest.java | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java index ffe82b7ced..c41423643a 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java @@ -8,11 +8,16 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Commit; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { HibernateConf.class }) @Transactional @@ -35,4 +40,146 @@ public class HibernateBootstrapIntegrationTest { Assert.assertNotNull(searchEntity); } + @Test + public void whenProgrammaticTransactionCommit_thenEntityIsInDatabase() { + assertTrue(TestTransaction.isActive()); + + //Save an entity and commit. + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + assertTrue(TestTransaction.isFlaggedForRollback()); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it, but don't commit. + TestTransaction.start(); + + assertTrue(TestTransaction.isFlaggedForRollback()); + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it and commit. + TestTransaction.start(); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + assertTrue(TestTransaction.isActive()); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is no longer there in a new transaction. + TestTransaction.start(); + + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNull(searchEntity); + } + + @Test + @Commit + public void givenTransactionCommitDefault_whenProgrammaticTransactionCommit_thenEntityIsInDatabase() { + assertTrue(TestTransaction.isActive()); + + //Save an entity and commit. + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + assertFalse(TestTransaction.isFlaggedForRollback()); + + TestTransaction.end(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it, but don't commit. + TestTransaction.start(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + TestTransaction.flagForRollback(); + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it and commit. + TestTransaction.start(); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + assertTrue(TestTransaction.isActive()); + + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is no longer there in a new transaction. + TestTransaction.start(); + + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNull(searchEntity); + } + } From 55660e770254f96e274c98801a0ad9a60e0489f0 Mon Sep 17 00:00:00 2001 From: myluckagain Date: Fri, 2 Feb 2018 03:17:53 +0500 Subject: [PATCH 090/118] BAEL-1497 (#3568) --- .../designpatterns/observer/Channel.java | 5 ++ .../designpatterns/observer/NewsAgency.java | 24 ++++++++++ .../designpatterns/observer/NewsChannel.java | 20 ++++++++ .../designpatterns/observer/ONewsAgency.java | 13 +++++ .../designpatterns/observer/ONewsChannel.java | 22 +++++++++ .../observer/PCLNewsAgency.java | 28 +++++++++++ .../observer/PCLNewsChannel.java | 21 +++++++++ .../observer/ObserverIntegrationTest.java | 47 +++++++++++++++++++ 8 files changed, 180 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java create mode 100644 core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java create mode 100644 core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java new file mode 100644 index 0000000000..9ca2edac38 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java @@ -0,0 +1,5 @@ +package com.baeldung.designpatterns.observer; + +public interface Channel { + public void update(Object o); +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java new file mode 100644 index 0000000000..0330fbdcdc --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java @@ -0,0 +1,24 @@ +package com.baeldung.designpatterns.observer; + +import java.util.ArrayList; +import java.util.List; + +public class NewsAgency { + private String news; + private List channels = new ArrayList<>(); + + public void addObserver(Channel channel) { + this.channels.add(channel); + } + + public void removeObserver(Channel channel) { + this.channels.remove(channel); + } + + public void setNews(String news) { + this.news = news; + for (Channel channel : this.channels) { + channel.update(this.news); + } + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java new file mode 100644 index 0000000000..09c22e0ad8 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java @@ -0,0 +1,20 @@ +package com.baeldung.designpatterns.observer; + +public class NewsChannel implements Channel { + + private String news; + + @Override + public void update(Object news) { + this.setNews((String) news); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } + +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java new file mode 100644 index 0000000000..2849820663 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java @@ -0,0 +1,13 @@ +package com.baeldung.designpatterns.observer; + +import java.util.Observable; + +public class ONewsAgency extends Observable { + private String news; + + public void setNews(String news) { + this.news = news; + setChanged(); + notifyObservers(news); + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java new file mode 100644 index 0000000000..3989fe0286 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java @@ -0,0 +1,22 @@ +package com.baeldung.designpatterns.observer; + +import java.util.Observable; +import java.util.Observer; + +public class ONewsChannel implements Observer { + + private String news; + + @Override + public void update(Observable o, Object news) { + this.setNews((String) news); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java new file mode 100644 index 0000000000..b05b97ab0b --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java @@ -0,0 +1,28 @@ +package com.baeldung.designpatterns.observer; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class PCLNewsAgency { + private String news; + + private PropertyChangeSupport support; + + public PCLNewsAgency() { + support = new PropertyChangeSupport(this); + } + + public void addPropertyChangeListener(PropertyChangeListener pcl) { + support.addPropertyChangeListener(pcl); + } + + public void removePropertyChangeListener(PropertyChangeListener pcl) { + support.removePropertyChangeListener(pcl); + } + + public void setNews(String value) { + support.firePropertyChange("news", this.news, value); + this.news = value; + + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java new file mode 100644 index 0000000000..ff8d35463c --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java @@ -0,0 +1,21 @@ +package com.baeldung.designpatterns.observer; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class PCLNewsChannel implements PropertyChangeListener { + + private String news; + + public void propertyChange(PropertyChangeEvent evt) { + this.setNews((String) evt.getNewValue()); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } +} diff --git a/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java b/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java new file mode 100644 index 0000000000..a8a0e29990 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java @@ -0,0 +1,47 @@ +package com.baeldung.designpatterns.observer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.baeldung.designpatterns.observer.NewsAgency; +import com.baeldung.designpatterns.observer.NewsChannel; + +public class ObserverIntegrationTest { + + @Test + public void whenChangingNewsAgencyState_thenNewsChannelNotified() { + + NewsAgency observable = new NewsAgency(); + NewsChannel observer = new NewsChannel(); + + observable.addObserver(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } + + @Test + public void whenChangingONewsAgencyState_thenONewsChannelNotified() { + + ONewsAgency observable = new ONewsAgency(); + ONewsChannel observer = new ONewsChannel(); + + observable.addObserver(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } + + @Test + public void whenChangingPCLNewsAgencyState_thenONewsChannelNotified() { + + PCLNewsAgency observable = new PCLNewsAgency(); + PCLNewsChannel observer = new PCLNewsChannel(); + + observable.addPropertyChangeListener(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } +} From c057808c6e9b86a97e1fbf5be8af050ed82a8f60 Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Fri, 2 Feb 2018 11:59:17 +0700 Subject: [PATCH 091/118] Modifies error messages --- .../assertj/custom/AssertJCustomAssertionsUnitTest.java | 4 ++-- .../com/baeldung/testing/assertj/custom/PersonAssert.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java index f9b5bad039..4c09311bac 100644 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java @@ -25,7 +25,7 @@ public class AssertJCustomAssertionsUnitTest { assertThat(person).isAdult(); fail(); } catch (AssertionError e) { - org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected adult but was juvenile"); + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected person to be adult"); } } @@ -38,7 +38,7 @@ public class AssertJCustomAssertionsUnitTest { assertThat(person).hasNickname("John"); fail(); } catch (AssertionError e) { - org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected nickname John but did not have"); + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected person to have nickname John"); } } } diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java index 4c071660f3..d6cc585e96 100644 --- a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java @@ -15,7 +15,7 @@ public class PersonAssert extends AbstractAssert { public PersonAssert hasFullName(String fullName) { isNotNull(); if (!actual.getFullName().equals(fullName)) { - failWithMessage("Expected full name %s but was %s", fullName, actual.getFullName()); + failWithMessage("Expected person to have full name %s but was %s", fullName, actual.getFullName()); } return this; } @@ -23,7 +23,7 @@ public class PersonAssert extends AbstractAssert { public PersonAssert isAdult() { isNotNull(); if (actual.getAge() < 18) { - failWithMessage("Expected adult but was juvenile"); + failWithMessage("Expected person to be adult"); } return this; } @@ -31,7 +31,7 @@ public class PersonAssert extends AbstractAssert { public PersonAssert hasNickname(String nickName) { isNotNull(); if (!actual.getNicknames().contains(nickName)) { - failWithMessage("Expected nickname %s but did not have", nickName); + failWithMessage("Expected person to have nickname %s", nickName); } return this; } From 2169be43014347905e0ff6a35e25020953c3bfc8 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Fri, 2 Feb 2018 15:40:03 +0100 Subject: [PATCH 092/118] Async refactor (#3570) --- .../AsyncHttpClientTestCase.java | 318 +++++++++--------- 1 file changed, 154 insertions(+), 164 deletions(-) diff --git a/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java index 7f9c2699fe..1398c2ba41 100644 --- a/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java +++ b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java @@ -1,15 +1,6 @@ package com.baeldung.asynchttpclient; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - +import io.netty.handler.codec.http.HttpHeaders; import org.asynchttpclient.AsyncCompletionHandler; import org.asynchttpclient.AsyncHandler; import org.asynchttpclient.AsyncHttpClient; @@ -27,193 +18,192 @@ import org.asynchttpclient.ws.WebSocketUpgradeHandler; import org.junit.Before; import org.junit.Test; -import io.netty.handler.codec.http.HttpHeaders; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class AsyncHttpClientTestCase { - private static AsyncHttpClient HTTP_CLIENT; + private static AsyncHttpClient HTTP_CLIENT; - @Before - public void setup() { + @Before + public void setup() { + AsyncHttpClientConfig clientConfig = Dsl.config().setConnectTimeout(15000).setRequestTimeout(15000).build(); + HTTP_CLIENT = Dsl.asyncHttpClient(clientConfig); + } - AsyncHttpClientConfig clientConfig = Dsl.config().setConnectTimeout(15000).setRequestTimeout(15000).build(); - HTTP_CLIENT = Dsl.asyncHttpClient(clientConfig); - } - - @Test - public void givenHttpClient_executeSyncGetRequest() { + @Test + public void givenHttpClient_executeSyncGetRequest() { - BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); - Future responseFuture = boundGetRequest.execute(); - try { - Response response = responseFuture.get(5000, TimeUnit.MILLISECONDS); - assertNotNull(response); - assertEquals(200, response.getStatusCode()); + Future responseFuture = boundGetRequest.execute(); + try { + Response response = responseFuture.get(5000, TimeUnit.MILLISECONDS); + assertNotNull(response); + assertEquals(200, response.getStatusCode()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); - } catch (TimeoutException e) { - e.printStackTrace(); - } - - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - @Test - public void givenHttpClient_executeAsyncGetRequest() { + @Test + public void givenHttpClient_executeAsyncGetRequest() { - // execute an unbound GET request - Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); - HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncCompletionHandler() { - @Override - public Integer onCompleted(Response response) throws Exception { + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) { - int resposeStatusCode = response.getStatusCode(); - assertEquals(200, resposeStatusCode); - return resposeStatusCode; - } - }); + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); - // execute a bound GET request - BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + // execute a bound GET request + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); - boundGetRequest.execute(new AsyncCompletionHandler() { - @Override - public Integer onCompleted(Response response) throws Exception { + boundGetRequest.execute(new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) { + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); - int resposeStatusCode = response.getStatusCode(); - assertEquals(200, resposeStatusCode); - return resposeStatusCode; - } - }); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + @Test + public void givenHttpClient_executeAsyncGetRequestWithAsyncHandler() { - @Test - public void givenHttpClient_executeAsyncGetRequestWithAsyncHandler() { + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); - // execute an unbound GET request - Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncHandler() { - HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncHandler() { + int responseStatusCode = -1; - int responseStatusCode = -1; + @Override + public State onStatusReceived(HttpResponseStatus responseStatus) { + responseStatusCode = responseStatus.getStatusCode(); + return State.CONTINUE; + } - @Override - public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - responseStatusCode = responseStatus.getStatusCode(); - return State.CONTINUE; - } + @Override + public State onHeadersReceived(HttpHeaders headers) { + return State.CONTINUE; + } - @Override - public State onHeadersReceived(HttpHeaders headers) throws Exception { - return State.CONTINUE; - } + @Override + public State onBodyPartReceived(HttpResponseBodyPart bodyPart) { + return State.CONTINUE; + } - @Override - public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - return State.CONTINUE; - } + @Override + public void onThrowable(Throwable t) { - @Override - public void onThrowable(Throwable t) { + } - } + @Override + public Integer onCompleted() { + assertEquals(200, responseStatusCode); + return responseStatusCode; + } + }); - @Override - public Integer onCompleted() throws Exception { - assertEquals(200, responseStatusCode); - return responseStatusCode; - } - }); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + @Test + public void givenHttpClient_executeAsyncGetRequestWithListanableFuture() { + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); - @Test - public void givenHttpClient_executeAsyncGetRequestWithListanableFuture() { - // execute an unbound GET request - Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + ListenableFuture listenableFuture = HTTP_CLIENT.executeRequest(unboundGetRequest); + listenableFuture.addListener(() -> { + Response response; + try { + response = listenableFuture.get(5000, TimeUnit.MILLISECONDS); + assertEquals(200, response.getStatusCode()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + }, Executors.newCachedThreadPool()); - ListenableFuture listenableFuture = HTTP_CLIENT.executeRequest(unboundGetRequest); - listenableFuture.addListener(() -> { - Response response; - try { - response = listenableFuture.get(5000, TimeUnit.MILLISECONDS); - assertEquals(200, response.getStatusCode()); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - e.printStackTrace(); - } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - }, Executors.newCachedThreadPool()); + @Test + public void givenWebSocketClient_tryToConnect() { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + WebSocketUpgradeHandler.Builder upgradeHandlerBuilder = new WebSocketUpgradeHandler.Builder(); + WebSocketUpgradeHandler wsHandler = upgradeHandlerBuilder.addWebSocketListener(new WebSocketListener() { + @Override + public void onOpen(WebSocket websocket) { + // WebSocket connection opened + } - @Test - public void givenWebSocketClient_tryToConnect() { + @Override + public void onClose(WebSocket websocket, int code, String reason) { + // WebSocket connection closed + } - WebSocketUpgradeHandler.Builder upgradeHandlerBuilder = new WebSocketUpgradeHandler.Builder(); - WebSocketUpgradeHandler wsHandler = upgradeHandlerBuilder.addWebSocketListener(new WebSocketListener() { - @Override - public void onOpen(WebSocket websocket) { - // WebSocket connection opened - } + @Override + public void onError(Throwable t) { + // WebSocket connection error + assertTrue(t.getMessage().contains("Request timeout")); + } + }).build(); - @Override - public void onClose(WebSocket websocket, int code, String reason) { - // WebSocket connection closed - } + WebSocket WEBSOCKET_CLIENT = null; + try { + WEBSOCKET_CLIENT = Dsl.asyncHttpClient() + .prepareGet("ws://localhost:5590/websocket") + .addHeader("header_name", "header_value") + .addQueryParam("key", "value") + .setRequestTimeout(5000) + .execute(wsHandler).get(); - @Override - public void onError(Throwable t) { - // WebSocket connection error - assertTrue(t.getMessage().contains("Request timeout")); - } - }).build(); - - WebSocket WEBSOCKET_CLIENT = null; - try { - WEBSOCKET_CLIENT = Dsl.asyncHttpClient() - .prepareGet("ws://localhost:5590/websocket") - .addHeader("header_name", "header_value") - .addQueryParam("key", "value") - .setRequestTimeout(5000) - .execute(wsHandler).get(); - - if (WEBSOCKET_CLIENT.isOpen()) { - WEBSOCKET_CLIENT.sendPingFrame(); - WEBSOCKET_CLIENT.sendTextFrame("test message"); - WEBSOCKET_CLIENT.sendBinaryFrame(new byte[] { 't', 'e', 's', 't' }); - } - - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } finally { - if (WEBSOCKET_CLIENT != null && WEBSOCKET_CLIENT.isOpen()) { - WEBSOCKET_CLIENT.sendCloseFrame(200, "OK"); - } - } - } + if (WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendPingFrame(); + WEBSOCKET_CLIENT.sendTextFrame("test message"); + WEBSOCKET_CLIENT.sendBinaryFrame(new byte[]{'t', 'e', 's', 't'}); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } finally { + if (WEBSOCKET_CLIENT != null && WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendCloseFrame(200, "OK"); + } + } + } } From fa906a2b59420fd29fca9fa93ae291771c84a8d4 Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Fri, 2 Feb 2018 21:01:22 +0100 Subject: [PATCH 093/118] removed Car class --- .../baeldung/testing/assertj/custom/Car.java | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java deleted file mode 100644 index e52ffee8e5..0000000000 --- a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Car.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.baeldung.testing.assertj.custom; - -public class Car { - private String type; - private Person owner; - - public Car(String type) { - this.type = type; - } - - public String getType() { - return type; - } - - public Person getOwner() { - return owner; - } - - public void setOwner(Person owner) { - this.owner = owner; - } -} From ff76cbc1fe12379b6e28ff63fb4ae80daa8a678e Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Sat, 3 Feb 2018 05:00:27 +0100 Subject: [PATCH 094/118] Bael 113 (#3484) * BAEL-399: A Guide to Multitenancy in Hibernate 5 * Removed unused properties in profile 2 * Changes after code review * BAEL-113 * Changes after code review * Added main method in spring boot application * Removed extra files --- pom.xml | 1 + spring-jinq/pom.xml | 83 +++++++++++++++++++ .../baeldung/spring/jinq/JinqApplication.java | 12 +++ .../config/JinqProviderConfiguration.java | 18 ++++ .../baeldung/spring/jinq/entities/Car.java | 58 +++++++++++++ .../spring/jinq/entities/Manufacturer.java | 42 ++++++++++ .../repositories/BaseJinqRepositoryImpl.java | 26 ++++++ .../jinq/repositories/CarRepository.java | 27 ++++++ .../jinq/repositories/CarRepositoryImpl.java | 72 ++++++++++++++++ .../src/main/resources/application.properties | 7 ++ .../CarRepositoryIntegrationTest.java | 42 ++++++++++ 11 files changed, 388 insertions(+) create mode 100644 spring-jinq/pom.xml create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java create mode 100644 spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java create mode 100644 spring-jinq/src/main/resources/application.properties create mode 100644 spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java diff --git a/pom.xml b/pom.xml index 4a25459fcb..582ee6696e 100644 --- a/pom.xml +++ b/pom.xml @@ -246,6 +246,7 @@ spring-zuul spring-reactor spring-vertx + spring-jinq spring-rest-embedded-tomcat diff --git a/spring-jinq/pom.xml b/spring-jinq/pom.xml new file mode 100644 index 0000000000..a895ae8dd4 --- /dev/null +++ b/spring-jinq/pom.xml @@ -0,0 +1,83 @@ + + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + 4.0.0 + spring-jinq + 0.1-SNAPSHOT + + spring-jinq + + jar + + + UTF-8 + 1.8 + + 1.8.22 + + + + + + org.springframework.boot + spring-boot-dependencies + 1.5.9.RELEASE + pom + import + + + + + + + org.jinq + jinq-jpa + ${jinq.version} + + + + + com.h2database + h2 + + + + org.hibernate + hibernate-entitymanager + + + + + org.springframework + spring-orm + + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + false + + + + + + diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java new file mode 100644 index 0000000000..d53b585d36 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.jinq; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JinqApplication { + + public static void main(String[] args) { + SpringApplication.run(JinqApplication.class, args); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java new file mode 100644 index 0000000000..6d921045b7 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.jinq.config; + +import javax.persistence.EntityManagerFactory; + +import org.jinq.jpa.JinqJPAStreamProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JinqProviderConfiguration { + + @Bean + @Autowired + JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) { + return new JinqJPAStreamProvider(emf); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java new file mode 100644 index 0000000000..263e6c7622 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java @@ -0,0 +1,58 @@ +package com.baeldung.spring.jinq.entities; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + +@Entity(name = "CAR") +public class Car { + private String model; + private String description; + private int year; + private String engine; + private Manufacturer manufacturer; + + @Id + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getEngine() { + return engine; + } + + public void setEngine(String engine) { + this.engine = engine; + } + + @OneToOne + @JoinColumn(name = "name") + public Manufacturer getManufacturer() { + return manufacturer; + } + + public void setManufacturer(Manufacturer manufacturer) { + this.manufacturer = manufacturer; + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java new file mode 100644 index 0000000000..f6e5fd23de --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.jinq.entities; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity(name = "MANUFACTURER") +public class Manufacturer { + + private String name; + private String city; + private List cars; + + @Id + 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; + } + + @OneToMany(mappedBy = "model") + public List getCars() { + return cars; + } + + public void setCars(List cars) { + this.cars = cars; + } + +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java new file mode 100644 index 0000000000..42b81ecc59 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.jinq.repositories; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.jinq.jpa.JPAJinqStream; +import org.jinq.jpa.JinqJPAStreamProvider; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class BaseJinqRepositoryImpl { + @Autowired + private JinqJPAStreamProvider jinqDataProvider; + + @PersistenceContext + private EntityManager entityManager; + + protected abstract Class entityType(); + + public JPAJinqStream stream() { + return streamOf(entityType()); + } + + protected JPAJinqStream streamOf(Class clazz) { + return jinqDataProvider.streamAll(entityManager, clazz); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java new file mode 100644 index 0000000000..56f6106e08 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java @@ -0,0 +1,27 @@ +package com.baeldung.spring.jinq.repositories; + +import java.util.List; +import java.util.Optional; + +import org.jinq.tuples.Pair; +import org.jinq.tuples.Tuple3; + +import com.baeldung.spring.jinq.entities.Car; +import com.baeldung.spring.jinq.entities.Manufacturer; + +public interface CarRepository { + + Optional findByModel(String model); + + List findByModelAndDescription(String model, String desc); + + List> findWithModelYearAndEngine(); + + Optional findManufacturerByModel(String model); + + List> findCarsPerManufacturer(); + + long countCarsByModel(String model); + + List findAll(int skip, int limit); +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java new file mode 100644 index 0000000000..bf16c87461 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java @@ -0,0 +1,72 @@ +package com.baeldung.spring.jinq.repositories; + +import java.util.List; +import java.util.Optional; + +import org.jinq.orm.stream.JinqStream; +import org.jinq.tuples.Pair; +import org.jinq.tuples.Tuple3; +import org.springframework.stereotype.Repository; + +import com.baeldung.spring.jinq.entities.Car; +import com.baeldung.spring.jinq.entities.Manufacturer; + +@Repository +public class CarRepositoryImpl extends BaseJinqRepositoryImpl implements CarRepository { + + @Override + public Optional findByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .findFirst(); + } + + @Override + public List findByModelAndDescription(String model, String desc) { + return stream().where(c -> c.getModel() + .equals(model) + && c.getDescription() + .contains(desc)) + .toList(); + } + + @Override + public List> findWithModelYearAndEngine() { + return stream().select(c -> new Tuple3<>(c.getModel(), c.getYear(), c.getEngine())) + .toList(); + } + + @Override + public Optional findManufacturerByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .select(c -> c.getManufacturer()) + .findFirst(); + } + + @Override + public List> findCarsPerManufacturer() { + return streamOf(Manufacturer.class).join(m -> JinqStream.from(m.getCars())) + .toList(); + } + + @Override + public long countCarsByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .count(); + } + + @Override + public List findAll(int skip, int limit) { + return stream().skip(skip) + .limit(limit) + .toList(); + } + + @Override + protected Class entityType() { + return Car.class; + } + +} diff --git a/spring-jinq/src/main/resources/application.properties b/spring-jinq/src/main/resources/application.properties new file mode 100644 index 0000000000..dc73bed0c5 --- /dev/null +++ b/spring-jinq/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:h2:~/jinq +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true \ No newline at end of file diff --git a/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java b/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java new file mode 100644 index 0000000000..9cb126cbaa --- /dev/null +++ b/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.jinq.repositories; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.jinq.JinqApplication; + +@ContextConfiguration(classes = JinqApplication.class) +@RunWith(SpringJUnit4ClassRunner.class) +public class CarRepositoryIntegrationTest { + + @Autowired + private CarRepository repository; + + @Test + public void givenACar_whenFilter_thenShouldBeFound() { + assertThat(repository.findByModel("model1") + .isPresent()).isFalse(); + } + + @Test + public void givenACar_whenMultipleFilters_thenShouldBeFound() { + assertThat(repository.findByModelAndDescription("model1", "desc") + .isEmpty()).isTrue(); + } + + @Test + public void whenUseASelectClause() { + assertThat(repository.findWithModelYearAndEngine() + .isEmpty()).isTrue(); + } + + @Test + public void whenUsingOneToOneRelationship() { + assertThat(repository.findManufacturerByModel("model1")).isNotNull(); + } +} From 710c25fb010068882767bccaaa8c73a70922adcd Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 3 Feb 2018 12:37:28 +0100 Subject: [PATCH 095/118] Ocheja fix (#3572) * Define beans for handling different message types in a lean chat app * Add class based spring beans configuration * Define spring configuration in XML for constructor based bean injection * Refactor package structure to separate constructor based bean injection code set from setter based bean injection code set * Define configuration and classes specific to setter-based bean injection. * Implement tests for constructor-based and setter-based bean injections * develop codes for explaining type erasure * Write unit tests for type erasure examples * Remove evaluation article code * Modify type erasure examples and unit tests * Modify type erasure examples and unit tests * Add expected exception in TypeErasureUnitTest * Correct grammar in class name * Implement File Manager app to demonstrate Polymorphism. Develop unit tests for Polymorphism article code * Add examples for static polymorphism * Change sysout statments to slf4j log info statements * Add assertions and expected errors check on Test * Add assertions and expected errors check on Test * Correct compile time error of symbol not found * Removed commented out non-compiling test. * Replace string concatenations with String.format * Replace string concatenations with String.format * Remove verbose file info descriptor and replace with simpler one * Add example codes for Hibernate Interceptors article Write tests for session-scoped and sessionFactory-scoped interceptors * Implement serializable on customInterceptorImpl * Implement examples for spring data with spring security integration * Remove webapp example implementations; too extensive --- .../interceptors/CustomInterceptorImpl.java | 2 +- spring-data-spring-security/README.md | 14 +++ spring-data-spring-security/pom.xml | 67 ++++++++++++ .../src/main/java/com/baeldung/AppConfig.java | 64 +++++++++++ .../com/baeldung/SpringSecurityConfig.java | 89 ++++++++++++++++ .../data/repositories/TweetRepository.java | 14 +++ .../data/repositories/UserRepository.java | 27 +++++ .../java/com/baeldung/models/AppUser.java | 83 +++++++++++++++ .../main/java/com/baeldung/models/Tweet.java | 63 +++++++++++ .../baeldung/security/AppUserPrincipal.java | 67 ++++++++++++ .../AuthenticationSuccessHandlerImpl.java | 28 +++++ .../security/CustomUserDetailsService.java | 40 +++++++ .../com/baeldung/util/DummyContentUtil.java | 63 +++++++++++ .../src/main/resources/application.properties | 0 .../main/resources/persistence-h2.properties | 8 ++ .../SpringDataWithSecurityTest.java | 100 ++++++++++++++++++ 16 files changed, 728 insertions(+), 1 deletion(-) create mode 100644 spring-data-spring-security/README.md create mode 100644 spring-data-spring-security/pom.xml create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java create mode 100644 spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java create mode 100644 spring-data-spring-security/src/main/resources/application.properties create mode 100644 spring-data-spring-security/src/main/resources/persistence-h2.properties create mode 100644 spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java index a84a981f7f..6736b39b64 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java @@ -9,7 +9,7 @@ import org.hibernate.Interceptor; import org.hibernate.Transaction; import org.hibernate.type.Type; -public class CustomInterceptorImpl implements Interceptor { +public class CustomInterceptorImpl implements Interceptor, Serializable { @Override public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException { diff --git a/spring-data-spring-security/README.md b/spring-data-spring-security/README.md new file mode 100644 index 0000000000..15b4b50870 --- /dev/null +++ b/spring-data-spring-security/README.md @@ -0,0 +1,14 @@ +# About this project +This project contains examples from the [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security) article from Baeldung. + +# Running the project +The application uses [Spring Boot](http://projects.spring.io/spring-boot/), so it is easy to run. You can start it any of a few ways: +* Run the `main` method from `SpringDataRestApplication` +* Use the Maven Spring Boot plugin: `mvn spring-boot:run` +* Package the application as a JAR and run it using `java -jar spring-data-spring-security.jar` + +# Viewing the running application +To view the running application, visit [http://localhost:8080](http://localhost:8080) in your browser + +###Relevant Articles: +- [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security) diff --git a/spring-data-spring-security/pom.xml b/spring-data-spring-security/pom.xml new file mode 100644 index 0000000000..d6b671ee57 --- /dev/null +++ b/spring-data-spring-security/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.baeldung + spring-data-spring-security + 1.0 + jar + + intro-spring-data-spring-security + Spring Data with Spring Security + + + parent-boot-5 + com.baeldung + 0.0.1-SNAPSHOT + ../parent-boot-5 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.security + spring-security-data + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + + + org.apache.tomcat.embed + tomcat-embed-jasper + + + + com.h2database + h2 + + + javax.servlet + jstl + + + + + ${project.artifactId} + + + + diff --git a/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java new file mode 100644 index 0000000000..16bbe8b326 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java @@ -0,0 +1,64 @@ +package com.baeldung; + +import java.util.Properties; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@SpringBootApplication +@PropertySource("classpath:persistence-h2.properties") +@EnableJpaRepositories(basePackages = { "com.baeldung.data.repositories" }) +@EnableWebMvc +@Import(SpringSecurityConfig.class) +public class AppConfig extends WebMvcConfigurerAdapter { + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("driverClassName")); + dataSource.setUrl(env.getProperty("url")); + dataSource.setUsername(env.getProperty("user")); + dataSource.setPassword(env.getProperty("password")); + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "com.baeldung.models" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + if (env.getProperty("hibernate.hbm2ddl.auto") != null) { + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + } + if (env.getProperty("hibernate.dialect") != null) { + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + } + if (env.getProperty("hibernate.show_sql") != null) { + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + } + return hibernateProperties; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java new file mode 100644 index 0000000000..ee13678a24 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java @@ -0,0 +1,89 @@ +package com.baeldung; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +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.builders.WebSecurity; +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.data.repository.query.SecurityEvaluationContextExtension; +import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.security.AuthenticationSuccessHandlerImpl; +import com.baeldung.security.CustomUserDetailsService; + +@Configuration +@EnableWebSecurity +@ComponentScan("com.baeldung.security") +public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private WebApplicationContext applicationContext; + private CustomUserDetailsService userDetailsService; + @Autowired + private AuthenticationSuccessHandlerImpl successHandler; + @Autowired + private DataSource dataSource; + + @PostConstruct + public void completeSetup() { + userDetailsService = applicationContext.getBean(CustomUserDetailsService.class); + } + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService) + .passwordEncoder(encoder()) + .and() + .authenticationProvider(authenticationProvider()) + .jdbcAuthentication() + .dataSource(dataSource); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers("/resources/**"); + } + + @Override + protected void configure(final HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/login") + .permitAll() + .and() + .formLogin() + .permitAll() + .successHandler(successHandler) + .and() + .csrf() + .disable(); + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(encoder()); + return authProvider; + } + + @Bean + public PasswordEncoder encoder() { + return new BCryptPasswordEncoder(11); + } + + @Bean + public SecurityEvaluationContextExtension securityEvaluationContextExtension() { + return new SecurityEvaluationContextExtension(); + } +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java new file mode 100644 index 0000000000..7d6446ed0d --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java @@ -0,0 +1,14 @@ +package com.baeldung.data.repositories; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.baeldung.models.Tweet; + +public interface TweetRepository extends PagingAndSortingRepository { + + @Query("select twt from Tweet twt JOIN twt.likes as lk where lk = ?#{ principal?.username } or twt.owner = ?#{ principal?.username }") + Page getMyTweetsAndTheOnesILiked(Pageable pageable); +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java new file mode 100644 index 0000000000..9f13c3197e --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java @@ -0,0 +1,27 @@ +package com.baeldung.data.repositories; + +import java.util.Date; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; + +public interface UserRepository extends CrudRepository { + AppUser findByUsername(String username); + + List findByName(String name); + + @Query("UPDATE AppUser u SET u.lastLogin=:lastLogin WHERE u.username = ?#{ principal?.username }") + @Modifying + @Transactional + public void updateLastLogin(@Param("lastLogin") Date lastLogin); +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java new file mode 100644 index 0000000000..e48233f90a --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java @@ -0,0 +1,83 @@ +package com.baeldung.models; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "users") +public class AppUser { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + + private String name; + @Column(unique = true) + private String username; + private String password; + private boolean enabled = true; + private Date lastLogin; + + private AppUser() { + } + + public AppUser(String name, String email, String password) { + this.username = email; + this.name = name; + this.password = password; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public Date getLastLogin() { + return lastLogin; + } + + public void setLastLogin(Date lastLogin) { + this.lastLogin = lastLogin; + } +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java new file mode 100644 index 0000000000..b2e45009f6 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java @@ -0,0 +1,63 @@ +package com.baeldung.models; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Tweet { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + private String tweet; + private String owner; + @ElementCollection(targetClass = String.class, fetch = FetchType.EAGER) + private Set likes = new HashSet(); + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + private Tweet() { + } + + public Tweet(String tweet, String owner) { + this.tweet = tweet; + this.owner = owner; + } + + public String getTweet() { + return tweet; + } + + public void setTweet(String tweet) { + this.tweet = tweet; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Set getLikes() { + return likes; + } + + public void setLikes(Set likes) { + this.likes = likes; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java new file mode 100644 index 0000000000..195f9f7bf6 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java @@ -0,0 +1,67 @@ +package com.baeldung.security; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.baeldung.models.AppUser; + +public class AppUserPrincipal implements UserDetails { + + private final AppUser user; + + // + + public AppUserPrincipal(AppUser user) { + this.user = user; + } + + // + + @Override + public String getUsername() { + return user.getUsername(); + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public Collection getAuthorities() { + final List authorities = Collections.singletonList(new SimpleGrantedAuthority("User")); + return authorities; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + // + + public AppUser getAppUser() { + return user; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java new file mode 100644 index 0000000000..3fc2bc6559 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java @@ -0,0 +1,28 @@ +package com.baeldung.security; + +import java.io.IOException; +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import com.baeldung.data.repositories.UserRepository; + +@Component +public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { + + @Autowired + private UserRepository userRepository; + + @Override + public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2) throws IOException, ServletException { + userRepository.updateLastLogin(new Date()); + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java new file mode 100644 index 0000000000..016f4f7fa9 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java @@ -0,0 +1,40 @@ +package com.baeldung.security; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +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; +import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.data.repositories.UserRepository; +import com.baeldung.models.AppUser; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + @Autowired + private WebApplicationContext applicationContext; + private UserRepository userRepository; + + public CustomUserDetailsService() { + super(); + } + + @PostConstruct + public void completeSetup() { + userRepository = applicationContext.getBean(UserRepository.class); + } + + @Override + public UserDetails loadUserByUsername(final String username) { + final AppUser appUser = userRepository.findByUsername(username); + if (appUser == null) { + throw new UsernameNotFoundException(username); + } + return new AppUserPrincipal(appUser); + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java new file mode 100644 index 0000000000..f1640264d2 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java @@ -0,0 +1,63 @@ +package com.baeldung.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; + +public class DummyContentUtil { + + public static final List generateDummyUsers() { + List appUsers = new ArrayList<>(); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + appUsers.add(new AppUser("Lionel Messi", "lionel@messi.com", passwordEncoder.encode("li1234"))); + appUsers.add(new AppUser("Cristiano Ronaldo", "cristiano@ronaldo.com", passwordEncoder.encode("c1234"))); + appUsers.add(new AppUser("Neymar Dos Santos", "neymar@neymar.com", passwordEncoder.encode("n1234"))); + appUsers.add(new AppUser("Luiz Suarez", "luiz@suarez.com", passwordEncoder.encode("lu1234"))); + appUsers.add(new AppUser("Andres Iniesta", "andres@iniesta.com", passwordEncoder.encode("a1234"))); + appUsers.add(new AppUser("Ivan Rakitic", "ivan@rakitic.com", passwordEncoder.encode("i1234"))); + appUsers.add(new AppUser("Ousman Dembele", "ousman@dembele.com", passwordEncoder.encode("o1234"))); + appUsers.add(new AppUser("Sergio Busquet", "sergio@busquet.com", passwordEncoder.encode("s1234"))); + appUsers.add(new AppUser("Gerard Pique", "gerard@pique.com", passwordEncoder.encode("g1234"))); + appUsers.add(new AppUser("Ter Stergen", "ter@stergen.com", passwordEncoder.encode("t1234"))); + return appUsers; + } + + public static final List generateDummyTweets(List users) { + List tweets = new ArrayList<>(); + Random random = new Random(); + IntStream.range(0, 9) + .sequential() + .forEach(i -> { + Tweet twt = new Tweet(String.format("Tweet %d", i), users.get(random.nextInt(users.size())) + .getUsername()); + twt.getLikes() + .addAll(users.subList(0, random.nextInt(users.size())) + .stream() + .map(AppUser::getUsername) + .collect(Collectors.toSet())); + tweets.add(twt); + }); + return tweets; + } + + public static Collection getAuthorities() { + Collection grantedAuthorities = new ArrayList(); + GrantedAuthority grantedAuthority = new GrantedAuthority() { + public String getAuthority() { + return "ROLE_USER"; + } + }; + grantedAuthorities.add(grantedAuthority); + return grantedAuthorities; + } + +} diff --git a/spring-data-spring-security/src/main/resources/application.properties b/spring-data-spring-security/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-data-spring-security/src/main/resources/persistence-h2.properties b/spring-data-spring-security/src/main/resources/persistence-h2.properties new file mode 100644 index 0000000000..a4b2af6361 --- /dev/null +++ b/spring-data-spring-security/src/main/resources/persistence-h2.properties @@ -0,0 +1,8 @@ +driverClassName=org.h2.Driver +url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 +username=sa +password= + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=false +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java new file mode 100644 index 0000000000..dbbfe7e85e --- /dev/null +++ b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java @@ -0,0 +1,100 @@ +package com.baeldung.relationships; + +import static org.springframework.util.Assert.isTrue; + +import java.util.Date; +import java.util.List; + +import javax.servlet.ServletContext; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import com.baeldung.AppConfig; +import com.baeldung.data.repositories.TweetRepository; +import com.baeldung.data.repositories.UserRepository; +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; +import com.baeldung.security.AppUserPrincipal; +import com.baeldung.util.DummyContentUtil; + +@RunWith(SpringRunner.class) +@WebAppConfiguration +@ContextConfiguration +@DirtiesContext +public class SpringDataWithSecurityTest { + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + @Autowired + private ServletContext servletContext; + private static UserRepository userRepository; + private static TweetRepository tweetRepository; + + @Before + public void testInit() { + ctx.register(AppConfig.class); + ctx.setServletContext(servletContext); + ctx.refresh(); + userRepository = ctx.getBean(UserRepository.class); + tweetRepository = ctx.getBean(TweetRepository.class); + List appUsers = (List) userRepository.save(DummyContentUtil.generateDummyUsers()); + tweetRepository.save(DummyContentUtil.generateDummyTweets(appUsers)); + } + + @AfterClass + public static void tearDown() { + tweetRepository.deleteAll(); + userRepository.deleteAll(); + } + + @Test + public void givenAppUser_whenLoginSuccessful_shouldUpdateLastLogin() { + AppUser appUser = userRepository.findByUsername("lionel@messi.com"); + Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities()); + SecurityContextHolder.getContext() + .setAuthentication(auth); + userRepository.updateLastLogin(new Date()); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void givenNoAppUserInSecurityContext_whenUpdateLastLoginAttempted_shouldFail() { + userRepository.updateLastLogin(new Date()); + } + + @Test + public void givenAppUser_whenLoginSuccessful_shouldReadMyPagedTweets() { + AppUser appUser = userRepository.findByUsername("lionel@messi.com"); + Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities()); + SecurityContextHolder.getContext() + .setAuthentication(auth); + Page page = null; + do { + page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5)); + for (Tweet twt : page.getContent()) { + isTrue((twt.getOwner() == appUser.getUsername()) || (twt.getLikes() + .contains(appUser.getUsername())), "I do not have any Tweets"); + } + } while (page.hasNext()); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void givenNoAppUser_whenPaginatedResultsRetrievalAttempted_shouldFail() { + Page page = null; + do { + page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5)); + } while (page != null && page.hasNext()); + } +} From b9ac0dc78b8e51e0bda526b420746227ab53eff3 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Sat, 3 Feb 2018 12:51:59 +0100 Subject: [PATCH 096/118] Updates after editor feedback --- .../ConsistentDateParameterValidator.java | 5 +---- .../constraints/ValidReservationValidator.java | 2 +- .../methodvalidation/model/Reservation.java | 4 ++++ .../model/ReservationManagement.java | 12 ++++++------ .../ContainerValidationIntegrationTest.java | 15 +++++++++++++-- .../ValidationIntegrationTest.java | 18 ++++++++++++++---- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java index b28abcba45..0ee1b6eda1 100644 --- a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java @@ -15,9 +15,6 @@ public class ConsistentDateParameterValidator implements ConstraintValidator getAllCustomers() { @@ -37,13 +42,8 @@ public class ReservationManagement { return null; } - public void createNewCustomer(@Valid Customer customer) { - - // ... - } - @Valid - public Customer getCustomerById() { + public Reservation getReservationById(int id) { return null; } diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java index 53133edd48..2363bf8f5d 100644 --- a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java @@ -1,6 +1,7 @@ package org.baeldung.javaxval.methodvalidation; import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; import org.baeldung.javaxval.methodvalidation.model.ReservationManagement; import org.junit.Rule; import org.junit.Test; @@ -69,9 +70,14 @@ public class ContainerValidationIntegrationTest { Customer customer = new Customer(); customer.setFirstName("John"); customer.setLastName("Doe"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); exception.expect(ConstraintViolationException.class); - reservationManagement.createNewCustomer(customer); + reservationManagement.createReservation(reservation); } @Test @@ -80,7 +86,12 @@ public class ContainerValidationIntegrationTest { Customer customer = new Customer(); customer.setFirstName("William"); customer.setLastName("Smith"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); - reservationManagement.createNewCustomer(customer); + reservationManagement.createReservation(reservation); } } diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java index 6a750698c6..6b53d3a107 100644 --- a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java @@ -172,11 +172,16 @@ public class ValidationIntegrationTest { public void whenValidationWithInvalidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { ReservationManagement object = new ReservationManagement(); - Method method = ReservationManagement.class.getMethod("createNewCustomer", Customer.class); + Method method = ReservationManagement.class.getMethod("createReservation", Reservation.class); Customer customer = new Customer(); customer.setFirstName("John"); customer.setLastName("Doe"); - Object[] parameterValues = { customer }; + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + Object[] parameterValues = { reservation }; Set> violations = executableValidator.validateParameters(object, method, parameterValues); assertEquals(2, violations.size()); @@ -186,11 +191,16 @@ public class ValidationIntegrationTest { public void whenValidationWithValidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { ReservationManagement object = new ReservationManagement(); - Method method = ReservationManagement.class.getMethod("createNewCustomer", Customer.class); + Method method = ReservationManagement.class.getMethod("createReservation", Reservation.class); Customer customer = new Customer(); customer.setFirstName("William"); customer.setLastName("Smith"); - Object[] parameterValues = { customer }; + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + Object[] parameterValues = { reservation }; Set> violations = executableValidator.validateParameters(object, method, parameterValues); assertEquals(0, violations.size()); From e15f2fc7dd347b42dbf414747cac8aa11e3c0b86 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Sat, 3 Feb 2018 13:10:31 +0100 Subject: [PATCH 097/118] Updates after editor feedback --- .../constraints/ValidReservationValidator.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java index a1cba9a5f6..fc225999be 100644 --- a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java @@ -27,14 +27,10 @@ public class ValidReservationValidator implements ConstraintValidator 0) { - - return true; - } - return false; + && reservation.getRoom() > 0); } } From 5a31640528ac3e7fce5314b06e194077b0cccd4b Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Sat, 3 Feb 2018 13:30:52 +0100 Subject: [PATCH 098/118] Updates after editor feedback --- .../org/baeldung/javaxval/methodvalidation/model/Customer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java index 529cf436da..fe9ad7080e 100644 --- a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java @@ -14,7 +14,7 @@ public class Customer { @Size(min = 5, max = 200) private String lastName; - public Customer(@Size(min = 5, max = 200) @NotNull String firstName, @Size(min = 5, max = 200) String lastName) { + public Customer(@Size(min = 5, max = 200) @NotNull String firstName, @Size(min = 5, max = 200) @NotNull String lastName) { this.firstName = firstName; this.lastName = lastName; } From 53bb9276100b3714cdf08cc49de91e17dc172a7e Mon Sep 17 00:00:00 2001 From: Nam Thai Nguyen Date: Sat, 3 Feb 2018 20:21:46 +0700 Subject: [PATCH 099/118] Modifies the Data class --- .../main/java/com/baeldung/javac/Data.java | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/core-java/src/main/java/com/baeldung/javac/Data.java b/core-java/src/main/java/com/baeldung/javac/Data.java index f6912fbd14..feef0c4f1e 100644 --- a/core-java/src/main/java/com/baeldung/javac/Data.java +++ b/core-java/src/main/java/com/baeldung/javac/Data.java @@ -1,28 +1,16 @@ package com.baeldung.javac; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; -public class Data implements Serializable { - static List textList = new ArrayList(); +public class Data { + List textList = new ArrayList(); - private static void addText() { - textList.add("baeldung"); - textList.add("."); - textList.add("com"); + public void addText(String text) { + textList.add(text); } public List getTextList() { - this.addText(); - List result = new ArrayList(); - String firstElement = (String) textList.get(0); - switch (firstElement) { - case "baeldung": - result.add("baeldung"); - case "com": - result.add("com"); - } - return result; + return this.textList; } -} +} \ No newline at end of file From ecf1cae3f7802bfb52e60777c95e9312ff7a5a53 Mon Sep 17 00:00:00 2001 From: Marcos Date: Sat, 3 Feb 2018 19:01:20 +0100 Subject: [PATCH 100/118] JPA Attribute Converters --- .../com/baeldung/hibernate/HibernateUtil.java | 1 + .../converters/PersonNameConverter.java | 43 +++++++++++ .../com/baeldung/hibernate/pojo/Person.java | 32 ++++++++ .../baeldung/hibernate/pojo/PersonName.java | 32 ++++++++ .../converter/PersonNameConverterTest.java | 73 +++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java create mode 100644 hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java index 25fc0d7b02..130edec8cc 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java @@ -81,6 +81,7 @@ public class HibernateUtil { metadataSources.addAnnotatedClass(Bag.class); metadataSources.addAnnotatedClass(PointEntity.class); metadataSources.addAnnotatedClass(PolygonEntity.class); + metadataSources.addAnnotatedClass(com.baeldung.hibernate.pojo.Person.class); Metadata metadata = metadataSources.buildMetadata(); return metadata.getSessionFactoryBuilder() diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java b/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java new file mode 100644 index 0000000000..c8b3397b09 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java @@ -0,0 +1,43 @@ +package com.baeldung.hibernate.converters; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import com.baeldung.hibernate.pojo.PersonName; + +@Converter +public class PersonNameConverter implements AttributeConverter { + + private static final String SEPARATOR = ", "; + + @Override + public String convertToDatabaseColumn(PersonName person) { + StringBuilder sb = new StringBuilder(); + if (person.getSurname() != null) { + sb.append(person.getSurname()); + sb.append(SEPARATOR); + } + + if (person.getName() != null) { + sb.append(person.getName()); + } + + return sb.toString(); + } + + @Override + public PersonName convertToEntityAttribute(String dbPerson) { + String[] pieces = dbPerson.split(SEPARATOR); + + if (pieces == null || pieces.length != 2) { + return null; + } + + PersonName personName = new PersonName(); + personName.setSurname(pieces[0]); + personName.setName(pieces[1]); + + return personName; + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java new file mode 100644 index 0000000000..390a5954ed --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java @@ -0,0 +1,32 @@ +package com.baeldung.hibernate.pojo; + +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import com.baeldung.hibernate.converters.PersonNameConverter; + +@Entity(name = "PersonTable") +public class Person { + + @Id + @GeneratedValue + private Long id; + + @Convert(converter = PersonNameConverter.class) + private PersonName personName; + + public PersonName getPersonName() { + return personName; + } + + public void setPersonName(PersonName personName) { + this.personName = personName; + } + + public Long getId() { + return id; + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java new file mode 100644 index 0000000000..689afd463a --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java @@ -0,0 +1,32 @@ +package com.baeldung.hibernate.pojo; + +import java.io.Serializable; + +import javax.persistence.Entity; + +@Entity +public class PersonName implements Serializable { + + private static final long serialVersionUID = 7883094644631050150L; + + private String name; + + private String surname; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + +} diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java new file mode 100644 index 0000000000..6eb89713f5 --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java @@ -0,0 +1,73 @@ +package com.baeldung.hibernate.converter; + +import java.io.IOException; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.hibernate.HibernateUtil; +import com.baeldung.hibernate.pojo.Person; +import com.baeldung.hibernate.pojo.PersonName; +import com.vividsolutions.jts.util.Assert; + +public class PersonNameConverterTest { + + private Session session; + private Transaction transaction; + + @Before + public void setUp() throws IOException { + session = HibernateUtil.getSessionFactory() + .openSession(); + transaction = session.beginTransaction(); + + session.createNativeQuery("delete from personTable") + .executeUpdate(); + + transaction.commit(); + transaction = session.beginTransaction(); + } + + @After + public void tearDown() { + transaction.rollback(); + session.close(); + } + + @Test + public void givenPersonName_WhenSaving_ThenNameAndSurnameConcat() { + final String name = "name"; + final String surname = "surname"; + + PersonName personName = new PersonName(); + personName.setName(name); + personName.setSurname(surname); + + Person person = new Person(); + person.setPersonName(personName); + + Long id = (Long) session.save(person); + + session.flush(); + session.clear(); + + String dbPersonName = (String) session.createNativeQuery("select p.personName from PersonTable p where p.id = :id") + .setParameter("id", id) + .getSingleResult(); + + Assert.equals(surname + ", " + name, dbPersonName); + + Person dbPerson = session.createNativeQuery("select * from PersonTable p where p.id = :id", Person.class) + .setParameter("id", id) + .getSingleResult(); + + Assert.equals(dbPerson.getPersonName() + .getName(), name); + Assert.equals(dbPerson.getPersonName() + .getSurname(), surname); + } + +} From 0bf31021884f5c40668fab1ccd3a3fbbdc62ead9 Mon Sep 17 00:00:00 2001 From: Marcos Date: Sat, 3 Feb 2018 19:25:48 +0100 Subject: [PATCH 101/118] code cleanup --- .../src/main/java/com/baeldung/hibernate/pojo/PersonName.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java index 689afd463a..335fe73f75 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java @@ -2,9 +2,6 @@ package com.baeldung.hibernate.pojo; import java.io.Serializable; -import javax.persistence.Entity; - -@Entity public class PersonName implements Serializable { private static final long serialVersionUID = 7883094644631050150L; From bf5b3045a1f6fe710a1ae63e935504a6139f05ba Mon Sep 17 00:00:00 2001 From: Eric Goebelbecker Date: Sat, 3 Feb 2018 22:49:11 -0500 Subject: [PATCH 102/118] BAEL-1486 - sample code for JGroups. Fixed a typo in InfluxDB. (#3578) --- influxdb/README.md | 1 - .../influxdb/InfluxDBConnectionLiveTest.java | 3 +- jgroups/README.md | 15 ++ jgroups/pom.xml | 36 +++ .../baeldung/jgroups/JGroupsMessenger.java | 222 ++++++++++++++++++ jgroups/src/main/resources/udp.xml | 48 ++++ pom.xml | 1 + 7 files changed, 323 insertions(+), 3 deletions(-) create mode 100644 jgroups/README.md create mode 100644 jgroups/pom.xml create mode 100644 jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java create mode 100644 jgroups/src/main/resources/udp.xml diff --git a/influxdb/README.md b/influxdb/README.md index 7d1684688d..f2c421580e 100644 --- a/influxdb/README.md +++ b/influxdb/README.md @@ -2,7 +2,6 @@ ### 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/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java index 6c7a03cb70..858903a676 100644 --- a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java +++ b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java @@ -8,7 +8,6 @@ import org.influxdb.dto.*; import org.influxdb.impl.InfluxDBResultMapper; import org.junit.Test; -import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -103,7 +102,7 @@ public class InfluxDBConnectionLiveTest { // another brief pause. Thread.sleep(10); - List memoryPointList = getPoints(connection, "Select * from memory", "baeldung"); + List memoryPointList = getPoints(connection, "Select * from memory", "baeldung"); assertEquals(10, memoryPointList.size()); diff --git a/jgroups/README.md b/jgroups/README.md new file mode 100644 index 0000000000..bb2813c3d6 --- /dev/null +++ b/jgroups/README.md @@ -0,0 +1,15 @@ +## Reliable Messaging with JGroups Tutorial Project + +### Relevant Article: +- [Reliable Messaging with JGroups](http://www.baeldung.com/reliable-messaging-with-jgroups/) + +### Overview +This Maven project contains the Java code for the article linked above. + +### Package Organization +Java classes for the intro tutorial are in the org.baeldung.jgroups package. + + +### Running the tests + +``` diff --git a/jgroups/pom.xml b/jgroups/pom.xml new file mode 100644 index 0000000000..0e5971875e --- /dev/null +++ b/jgroups/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + jgroups + 0.1-SNAPSHOT + jar + jgroups + Reliable Messaging with JGroups Tutorial + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.jgroups + jgroups + 4.0.10.Final + + + + commons-cli + commons-cli + 1.4 + + + + + 1.8 + UTF-8 + + + diff --git a/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java new file mode 100644 index 0000000000..2a8df8a9ab --- /dev/null +++ b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java @@ -0,0 +1,222 @@ +package com.baeldung.jgroups; + +import org.apache.commons.cli.*; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.List; +import java.util.Optional; + +public class JGroupsMessenger extends ReceiverAdapter { + + private JChannel channel; + private String userName; + private String clusterName; + private View lastView; + private boolean running = true; + + // Our shared state + private Integer messageCount = 0; + + /** + * Connect to a JGroups cluster using command line options + * @param args command line arguments + * @throws Exception + */ + private void start(String[] args) throws Exception { + processCommandline(args); + + // Create the channel + // This file could be moved, or made a command line option. + channel = new JChannel("src/main/resources/udp.xml"); + + // Set a name + channel.name(userName); + + // Register for callbacks + channel.setReceiver(this); + + // Ignore our messages + channel.setDiscardOwnMessages(true); + + // Connect + channel.connect(clusterName); + + // Start state transfer + channel.getState(null, 0); + + // Do the things + processInput(); + + // Clean up + channel.close(); + + } + + /** + * Quick and dirty implementaton of commons cli for command line args + * @param args the command line args + * @throws ParseException + */ + private void processCommandline(String[] args) throws ParseException { + + // Options, parser, friendly help + Options options = new Options(); + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + + options.addOption("u", "user", true, "User name") + .addOption("c", "cluster", true, "Cluster name"); + + CommandLine line = parser.parse(options, args); + + if (line.hasOption("user")) { + userName = line.getOptionValue("user"); + } else { + formatter.printHelp("JGroupsMessenger: need a user name.\n", options); + System.exit(-1); + } + + if (line.hasOption("cluster")) { + clusterName = line.getOptionValue("cluster"); + } else { + formatter.printHelp("JGroupsMessenger: need a cluster name.\n", options); + System.exit(-1); + } + } + + // Start it up + public static void main(String[] args) throws Exception { + new JGroupsMessenger().start(args); + } + + + @Override + public void viewAccepted(View newView) { + + // Save view if this is the first + if (lastView == null) { + System.out.println("Received initial view:"); + newView.forEach(System.out::println); + } else { + + // Compare to last view + System.out.println("Received new view."); + + List
newMembers = View.newMembers(lastView, newView); + if (newMembers.size() > 0) { + System.out.println("New members: "); + newMembers.forEach(System.out::println); + } + + List
exMembers = View.leftMembers(lastView, newView); + if (exMembers.size() > 0) { + System.out.println("Exited members:"); + exMembers.forEach(System.out::println); + } + } + lastView = newView; + } + + /** + * Loop on console input until we see 'x' to exit + */ + private void processInput() { + + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + while (running) { + try { + + // Get a destination, means broadcast + Address destination = null; + System.out.print("Enter a destination: "); + System.out.flush(); + String destinationName = in.readLine().toLowerCase(); + + if (destinationName.equals("x")) { + running = false; + continue; + } else if (!destinationName.isEmpty()) { + Optional
optDestination = getAddress(destinationName); + if (optDestination.isPresent()) { + destination = optDestination.get(); + } else { + System.out.println("Destination not found, try again."); + continue; + } + } + + // Accept a string to send + System.out.print("Enter a message: "); + System.out.flush(); + String line = in.readLine().toLowerCase(); + sendMessage(destination, line); + } catch (IOException ioe) { + running = false; + } + } + System.out.println("Exiting."); + } + + /** + * Send message from here + * @param destination the destination + * @param messageString the message + */ + private void sendMessage(Address destination, String messageString) { + try { + System.out.println("Sending " + messageString + " to " + destination); + Message message = new Message(destination, messageString); + channel.send(message); + } catch (Exception exception) { + System.err.println("Exception sending message: " + exception.getMessage()); + running = false; + } + } + + @Override + public void receive(Message message) { + // Print source and dest with message + String line = "Message received from: " + message.getSrc() + " to: " + message.getDest() + " -> " + message.getObject(); + + // Only track the count of broadcast messages + // Tracking direct message would make for a pointless state + if (message.getDest() == null) { + messageCount++; + System.out.println("Message count: " + messageCount); + } + + System.out.println(line); + } + + @Override + public void getState(OutputStream output) throws Exception { + // Serialize into the stream + Util.objectToStream(messageCount, new DataOutputStream(output)); + } + + @Override + public void setState(InputStream input) { + + // NOTE: since we know that incrementing the count and transferring the state + // is done inside the JChannel's thread, we don't have to worry about synchronizing + // messageCount. For production code it should be synchronized! + try { + // Deserialize + messageCount = Util.objectFromStream(new DataInputStream(input)); + } catch (Exception e) { + System.out.println("Error deserialing state!"); + } + System.out.println(messageCount + " is the current messagecount."); + } + + + private Optional
getAddress(String name) { + View view = channel.view(); + return view.getMembers().stream().filter(address -> name.equals(address.toString())).findFirst(); + } + +} + + diff --git a/jgroups/src/main/resources/udp.xml b/jgroups/src/main/resources/udp.xml new file mode 100644 index 0000000000..5a9bfd0851 --- /dev/null +++ b/jgroups/src/main/resources/udp.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 582ee6696e..ca6d4afe82 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ javax-servlets javaxval jaxb + jgroups jee-7 jjwt From 796277bdaa4faf45cf549ee58b18dee56e872564 Mon Sep 17 00:00:00 2001 From: Marcos Date: Sun, 4 Feb 2018 14:06:11 +0100 Subject: [PATCH 103/118] fix assert --- .../hibernate/converter/PersonNameConverterTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java index 6eb89713f5..aec2311294 100644 --- a/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java +++ b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java @@ -11,7 +11,8 @@ import org.junit.Test; import com.baeldung.hibernate.HibernateUtil; import com.baeldung.hibernate.pojo.Person; import com.baeldung.hibernate.pojo.PersonName; -import com.vividsolutions.jts.util.Assert; + +import static org.junit.Assert.assertEquals; public class PersonNameConverterTest { @@ -58,15 +59,15 @@ public class PersonNameConverterTest { .setParameter("id", id) .getSingleResult(); - Assert.equals(surname + ", " + name, dbPersonName); + assertEquals(surname + ", " + name, dbPersonName); Person dbPerson = session.createNativeQuery("select * from PersonTable p where p.id = :id", Person.class) .setParameter("id", id) .getSingleResult(); - Assert.equals(dbPerson.getPersonName() + assertEquals(dbPerson.getPersonName() .getName(), name); - Assert.equals(dbPerson.getPersonName() + assertEquals(dbPerson.getPersonName() .getSurname(), surname); } From 84782d054e429e5d71a4bda0d18029edd1f61ac3 Mon Sep 17 00:00:00 2001 From: abialas Date: Sun, 4 Feb 2018 23:44:22 +0100 Subject: [PATCH 104/118] BAEL-21 (#3587) * BAEL-1412 add java 8 spring data features * BAEL-21 new HTTP API overview * BAEL-21 fix executor --- .../com/baeldung/java9/httpclient/HttpClientTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java index a4c6ac0d7d..5cf3b9098f 100644 --- a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; @@ -116,18 +117,20 @@ public class HttpClientTest { .GET() .build(); + ExecutorService executorService = Executors.newFixedThreadPool(2); + CompletableFuture> response1 = HttpClient.newBuilder() - .executor(Executors.newFixedThreadPool(2)) + .executor(executorService) .build() .sendAsync(request, HttpResponse.BodyHandler.asString()); CompletableFuture> response2 = HttpClient.newBuilder() - .executor(Executors.newFixedThreadPool(2)) + .executor(executorService) .build() .sendAsync(request, HttpResponse.BodyHandler.asString()); CompletableFuture> response3 = HttpClient.newBuilder() - .executor(Executors.newFixedThreadPool(2)) + .executor(executorService) .build() .sendAsync(request, HttpResponse.BodyHandler.asString()); From c03e0919e1195bdc06139a930220568dfefa72b1 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Mon, 5 Feb 2018 10:21:36 +0100 Subject: [PATCH 105/118] Updates after editor feedback --- javaxval/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/javaxval/pom.xml b/javaxval/pom.xml index 0891fe6959..a05ee43aaf 100644 --- a/javaxval/pom.xml +++ b/javaxval/pom.xml @@ -22,12 +22,6 @@ - - javax.validation - validation-api - ${validation-api.version} - - org.hibernate hibernate-validator From 7ecceaf5e25ba9d1b549bf2bfad60f592a8677c2 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Mon, 5 Feb 2018 11:17:37 +0100 Subject: [PATCH 106/118] Updates after editor feedback --- .../constraints/ConsistentDateParameterValidator.java | 6 +----- .../constraints/ValidReservationValidator.java | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java index 0ee1b6eda1..f1c97760d7 100644 --- a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java @@ -9,15 +9,11 @@ import java.time.LocalDate; @SupportedValidationTarget(ValidationTarget.PARAMETERS) public class ConsistentDateParameterValidator implements ConstraintValidator { - @Override - public void initialize(ConsistentDateParameters constraintAnnotation) { - } - @Override public boolean isValid(Object[] value, ConstraintValidatorContext context) { if (value[0] == null || value[1] == null) { - return false; + return true; } if (!(value[0] instanceof LocalDate) || !(value[1] instanceof LocalDate)) { diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java index fc225999be..7b730480ed 100644 --- a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java @@ -8,15 +8,11 @@ import java.time.LocalDate; public class ValidReservationValidator implements ConstraintValidator { - @Override - public void initialize(ValidReservation constraintAnnotation) { - } - @Override public boolean isValid(Reservation reservation, ConstraintValidatorContext context) { if (reservation == null) { - return false; + return true; } if (!(reservation instanceof Reservation)) { From a6a291ce22607fc8e7d50f4a90bb1c1f1e649d06 Mon Sep 17 00:00:00 2001 From: daoire Date: Mon, 5 Feb 2018 12:18:04 +0000 Subject: [PATCH 107/118] Update README --- libraries/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/README.md b/libraries/README.md index d001f13698..fbf2b4e876 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -59,6 +59,7 @@ - [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) +- [Programatically Create, Configure, and Run a Tomcat Server] (http://www.baeldung.com/tomcat-programmatic-setup) From 34e8c290fd57bbe98646ff50a423fd4f863b7f2d Mon Sep 17 00:00:00 2001 From: daoire Date: Mon, 5 Feb 2018 12:30:23 +0000 Subject: [PATCH 108/118] Update README.md --- logging-modules/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/logging-modules/README.md b/logging-modules/README.md index 8ae7316047..6de71adb43 100644 --- a/logging-modules/README.md +++ b/logging-modules/README.md @@ -4,3 +4,4 @@ ### Relevant Articles: - [Creating a Custom Logback Appender](http://www.baeldung.com/custom-logback-appender) +- [Get Log Output in JSON Format](http://www.baeldung.com/java-log-json-output) From fcb114b50c1cf920085d55c2c1acd2785d12c0ed Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Mon, 5 Feb 2018 20:55:45 +0200 Subject: [PATCH 109/118] Update README.md --- mesos-marathon/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesos-marathon/README.md b/mesos-marathon/README.md index 3223eb2478..164f0db5d3 100644 --- a/mesos-marathon/README.md +++ b/mesos-marathon/README.md @@ -1,3 +1,5 @@ ### Relevant articles - [Simple Jenkins Pipeline with Marathon and Mesos](http://www.baeldung.com/jenkins-pipeline-with-marathon-mesos) + +To run the pipeline, please modify the dockerise.sh file with your own useranema and password for docker login. From 43d0f4c15c6b8813a86305ba9c218331458f6d92 Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Mon, 5 Feb 2018 20:10:44 +0100 Subject: [PATCH 110/118] added link to article --- testing-modules/testing/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/testing-modules/testing/README.md b/testing-modules/testing/README.md index 3511bb1bb9..2894da3496 100644 --- a/testing-modules/testing/README.md +++ b/testing-modules/testing/README.md @@ -17,3 +17,4 @@ - [Introduction to Jukito](http://www.baeldung.com/jukito) - [Custom JUnit 4 Test Runners](http://www.baeldung.com/junit-4-custom-runners) - [Guide to JSpec](http://www.baeldung.com/jspec) +- [Custom Assertions with AssertJ](http://www.baeldung.com/assertj-custom-assertion) From 1011edf288140b87a68341f05b2774e153ffbac3 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Mon, 5 Feb 2018 21:16:09 +0200 Subject: [PATCH 111/118] Update README.md --- mesos-marathon/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesos-marathon/README.md b/mesos-marathon/README.md index 164f0db5d3..1d4d4995a8 100644 --- a/mesos-marathon/README.md +++ b/mesos-marathon/README.md @@ -2,4 +2,4 @@ - [Simple Jenkins Pipeline with Marathon and Mesos](http://www.baeldung.com/jenkins-pipeline-with-marathon-mesos) -To run the pipeline, please modify the dockerise.sh file with your own useranema and password for docker login. + To run the pipeline, please modify the dockerise.sh file with your own useranema and password for docker login. From 372cba10bb23e5e1fc50631c63dfa5ad4da6db20 Mon Sep 17 00:00:00 2001 From: Eric Goebelbecker Date: Mon, 5 Feb 2018 19:47:17 -0500 Subject: [PATCH 112/118] BAEL-1486 - small changes to JGroups (#3590) * BAEL-1486 - sample code for JGroups. Fixed a typo in InfluxDB. * BAEL-1486 - updates for JGroups. --- .../baeldung/jgroups/JGroupsMessenger.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java index 2a8df8a9ab..70eacabf1e 100644 --- a/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java +++ b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java @@ -100,21 +100,16 @@ public class JGroupsMessenger extends ReceiverAdapter { System.out.println("Received initial view:"); newView.forEach(System.out::println); } else { - // Compare to last view System.out.println("Received new view."); List
newMembers = View.newMembers(lastView, newView); - if (newMembers.size() > 0) { - System.out.println("New members: "); - newMembers.forEach(System.out::println); - } + System.out.println("New members: "); + newMembers.forEach(System.out::println); List
exMembers = View.leftMembers(lastView, newView); - if (exMembers.size() > 0) { - System.out.println("Exited members:"); - exMembers.forEach(System.out::println); - } + System.out.println("Exited members:"); + exMembers.forEach(System.out::println); } lastView = newView; } @@ -122,7 +117,7 @@ public class JGroupsMessenger extends ReceiverAdapter { /** * Loop on console input until we see 'x' to exit */ - private void processInput() { + private void processInput() throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); while (running) { @@ -138,13 +133,8 @@ public class JGroupsMessenger extends ReceiverAdapter { running = false; continue; } else if (!destinationName.isEmpty()) { - Optional
optDestination = getAddress(destinationName); - if (optDestination.isPresent()) { - destination = optDestination.get(); - } else { - System.out.println("Destination not found, try again."); - continue; - } + destination = getAddress(destinationName) + . orElseThrow(() -> new Exception("Destination not found")); } // Accept a string to send From 56316fd029ff941e39e5bdf7dc3f5cca170946b0 Mon Sep 17 00:00:00 2001 From: linhvovn Date: Wed, 7 Feb 2018 03:43:44 +0800 Subject: [PATCH 113/118] [tlinh2110-BAEL1382] Add Security in SI (#3593) * [tlinh2110-BAEL1382] Add Security in SI * [tlinh2110-BAEL1382] Upgrade to Spring 5 & add Logger --- spring-integration/pom.xml | 41 ++++++++-- .../baeldung/si/security/MessageConsumer.java | 45 ++++++++++ .../si/security/SecuredDirectChannel.java | 50 +++++++++++ .../baeldung/si/security/SecurityConfig.java | 46 +++++++++++ .../si/security/SecurityPubSubChannel.java | 82 +++++++++++++++++++ .../security/UsernameAccessDecisionVoter.java | 45 ++++++++++ .../si/TestSpringIntegrationSecurity.java | 81 ++++++++++++++++++ ...TestSpringIntegrationSecurityExecutor.java | 68 +++++++++++++++ 8 files changed, 452 insertions(+), 6 deletions(-) create mode 100644 spring-integration/src/main/java/com/baeldung/si/security/MessageConsumer.java create mode 100644 spring-integration/src/main/java/com/baeldung/si/security/SecuredDirectChannel.java create mode 100644 spring-integration/src/main/java/com/baeldung/si/security/SecurityConfig.java create mode 100644 spring-integration/src/main/java/com/baeldung/si/security/SecurityPubSubChannel.java create mode 100644 spring-integration/src/main/java/com/baeldung/si/security/UsernameAccessDecisionVoter.java create mode 100644 spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurity.java create mode 100644 spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurityExecutor.java diff --git a/spring-integration/pom.xml b/spring-integration/pom.xml index 4e210122b0..27cc7381e3 100644 --- a/spring-integration/pom.xml +++ b/spring-integration/pom.xml @@ -18,7 +18,7 @@ UTF-8 - 4.3.5.RELEASE + 5.0.1.RELEASE 1.1.4.RELEASE 1.4.7 1.1.1 @@ -68,7 +68,7 @@ org.springframework.integration spring-integration-core - ${spring.integration.version} + ${spring.version} javax.activation @@ -84,17 +84,17 @@ org.springframework.integration spring-integration-twitter - ${spring.integration.version} + ${spring.version} org.springframework.integration spring-integration-mail - ${spring.integration.version} + ${spring.version} org.springframework.integration spring-integration-ftp - ${spring.integration.version} + ${spring.version} org.springframework.social @@ -104,7 +104,36 @@ org.springframework.integration spring-integration-file - ${spring.integration.version} + ${spring.version} + + + + org.springframework.security + spring-security-core + ${spring.version} + + + org.springframework.security + spring-security-config + ${spring.version} + + + org.springframework.integration + spring-integration-security + ${spring.version} + + + + org.springframework.security + spring-security-test + ${spring.version} + test + + + org.springframework + spring-test + ${spring.version} + test junit diff --git a/spring-integration/src/main/java/com/baeldung/si/security/MessageConsumer.java b/spring-integration/src/main/java/com/baeldung/si/security/MessageConsumer.java new file mode 100644 index 0000000000..af925c63a7 --- /dev/null +++ b/spring-integration/src/main/java/com/baeldung/si/security/MessageConsumer.java @@ -0,0 +1,45 @@ +package com.baeldung.si.security; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Service; + +@Service +public class MessageConsumer { + + private String messageContent; + + private Map messagePSContent = new ConcurrentHashMap<>(); + + public String getMessageContent() { + return messageContent; + } + + public void setMessageContent(String messageContent) { + this.messageContent = messageContent; + } + + public Map getMessagePSContent() { + return messagePSContent; + } + + public void setMessagePSContent(Map messagePSContent) { + this.messagePSContent = messagePSContent; + } + + @ServiceActivator(inputChannel = "endDirectChannel") + public void endDirectFlow(Message message) { + setMessageContent(message.getPayload().toString()); + } + + @ServiceActivator(inputChannel = "finalPSResult") + public void endPSFlow(Message message) { + Logger.getAnonymousLogger().info(Thread.currentThread().getName() + " has completed ---------------------------"); + messagePSContent.put(Thread.currentThread().getName(), (String) message.getPayload()); + } + +} diff --git a/spring-integration/src/main/java/com/baeldung/si/security/SecuredDirectChannel.java b/spring-integration/src/main/java/com/baeldung/si/security/SecuredDirectChannel.java new file mode 100644 index 0000000000..964a07d7d7 --- /dev/null +++ b/spring-integration/src/main/java/com/baeldung/si/security/SecuredDirectChannel.java @@ -0,0 +1,50 @@ +package com.baeldung.si.security; + +import java.util.logging.Logger; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.security.channel.ChannelSecurityInterceptor; +import org.springframework.integration.security.channel.SecuredChannel; +import org.springframework.messaging.Message; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.AuthenticationManager; + +@Configuration +@EnableIntegration +public class SecuredDirectChannel { + + @Bean(name = "startDirectChannel") + @SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = { "ROLE_VIEWER", "jane" }) + public DirectChannel startDirectChannel() { + return new DirectChannel(); + } + + @ServiceActivator(inputChannel = "startDirectChannel", outputChannel = "endDirectChannel") + @PreAuthorize("hasRole('ROLE_LOGGER')") + public Message logMessage(Message message) { + Logger.getAnonymousLogger().info(message.toString()); + return message; + } + + @Bean(name = "endDirectChannel") + @SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = { "ROLE_EDITOR" }) + public DirectChannel endDirectChannel() { + return new DirectChannel(); + } + + @Autowired + @Bean + public ChannelSecurityInterceptor channelSecurityInterceptor(AuthenticationManager authenticationManager, AccessDecisionManager customAccessDecisionManager) { + ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor(); + channelSecurityInterceptor.setAuthenticationManager(authenticationManager); + channelSecurityInterceptor.setAccessDecisionManager(customAccessDecisionManager); + return channelSecurityInterceptor; + } + +} diff --git a/spring-integration/src/main/java/com/baeldung/si/security/SecurityConfig.java b/spring-integration/src/main/java/com/baeldung/si/security/SecurityConfig.java new file mode 100644 index 0000000000..9c5b38b909 --- /dev/null +++ b/spring-integration/src/main/java/com/baeldung/si/security/SecurityConfig.java @@ -0,0 +1,46 @@ +package com.baeldung.si.security; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.security.channel.ChannelSecurityInterceptor; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.AccessDecisionVoter; +import org.springframework.security.access.vote.AffirmativeBased; +import org.springframework.security.access.vote.RoleVoter; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfig extends GlobalMethodSecurityConfiguration { + + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } + + @Bean + public AccessDecisionManager customAccessDecisionManager() { + List> decisionVoters = new ArrayList<>(); + decisionVoters.add(new RoleVoter()); + decisionVoters.add(new UsernameAccessDecisionVoter()); + AccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters); + return accessDecisionManager; + } + + @Autowired + @Bean + public ChannelSecurityInterceptor channelSecurityInterceptor(AuthenticationManager authenticationManager, AccessDecisionManager customAccessDecisionManager) { + ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor(); + channelSecurityInterceptor.setAuthenticationManager(authenticationManager); + channelSecurityInterceptor.setAccessDecisionManager(customAccessDecisionManager); + return channelSecurityInterceptor; + } + +} diff --git a/spring-integration/src/main/java/com/baeldung/si/security/SecurityPubSubChannel.java b/spring-integration/src/main/java/com/baeldung/si/security/SecurityPubSubChannel.java new file mode 100644 index 0000000000..11409bb89b --- /dev/null +++ b/spring-integration/src/main/java/com/baeldung/si/security/SecurityPubSubChannel.java @@ -0,0 +1,82 @@ +package com.baeldung.si.security; + +import java.util.stream.Collectors; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.PublishSubscribeChannel; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.config.GlobalChannelInterceptor; +import org.springframework.integration.security.channel.SecuredChannel; +import org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor; +import org.springframework.integration.support.DefaultMessageBuilderFactory; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +@Configuration +@EnableIntegration +public class SecurityPubSubChannel { + + @Bean(name = "startPSChannel") + @SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_VIEWER") + public PublishSubscribeChannel startChannel() { + return new PublishSubscribeChannel(executor()); + } + + @ServiceActivator(inputChannel = "startPSChannel", outputChannel = "finalPSResult") + @PreAuthorize("hasRole('ROLE_LOGGER')") + public Message changeMessageToRole(Message message) { + return buildNewMessage(getRoles(), message); + } + + @ServiceActivator(inputChannel = "startPSChannel", outputChannel = "finalPSResult") + @PreAuthorize("hasRole('ROLE_VIEWER')") + public Message changeMessageToUserName(Message message) { + return buildNewMessage(getUsername(), message); + } + + @Bean(name = "finalPSResult") + public DirectChannel finalPSResult() { + return new DirectChannel(); + } + + @Bean + @GlobalChannelInterceptor(patterns = { "startPSChannel", "endDirectChannel" }) + public ChannelInterceptor securityContextPropagationInterceptor() { + return new SecurityContextPropagationChannelInterceptor(); + } + + @Bean + public ThreadPoolTaskExecutor executor() { + ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor(); + pool.setCorePoolSize(10); + pool.setMaxPoolSize(10); + pool.setWaitForTasksToCompleteOnShutdown(true); + return pool; + } + + public String getRoles() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getAuthorities().stream().map(auth -> auth.getAuthority()).collect(Collectors.joining(",")); + } + + public String getUsername() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getName(); + } + + public Message buildNewMessage(String content, Message message) { + DefaultMessageBuilderFactory builderFactory = new DefaultMessageBuilderFactory(); + MessageBuilder messageBuilder = builderFactory.withPayload(content); + messageBuilder.copyHeaders(message.getHeaders()); + return messageBuilder.build(); + } + +} diff --git a/spring-integration/src/main/java/com/baeldung/si/security/UsernameAccessDecisionVoter.java b/spring-integration/src/main/java/com/baeldung/si/security/UsernameAccessDecisionVoter.java new file mode 100644 index 0000000000..052f79ddf7 --- /dev/null +++ b/spring-integration/src/main/java/com/baeldung/si/security/UsernameAccessDecisionVoter.java @@ -0,0 +1,45 @@ +package com.baeldung.si.security; + +import java.util.Collection; + +import org.springframework.security.access.AccessDecisionVoter; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.core.Authentication; + +public class UsernameAccessDecisionVoter implements AccessDecisionVoter { + private String rolePrefix = "ROLE_"; + + @Override + public boolean supports(ConfigAttribute attribute) { + if ((attribute.getAttribute() != null) + && !attribute.getAttribute().startsWith(rolePrefix)) { + return true; + }else { + return false; + } + } + + @Override + public boolean supports(Class clazz) { + return true; + } + + @Override + public int vote(Authentication authentication, Object object, Collection attributes) { + if (authentication == null) { + return ACCESS_DENIED; + } + String name = authentication.getName(); + int result = ACCESS_ABSTAIN; + for (ConfigAttribute attribute : attributes) { + if (this.supports(attribute)) { + result = ACCESS_DENIED; + if (attribute.getAttribute().equals(name)) { + return ACCESS_GRANTED; + } + } + } + return result; + } + +} diff --git a/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurity.java b/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurity.java new file mode 100644 index 0000000000..45dcb1e836 --- /dev/null +++ b/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurity.java @@ -0,0 +1,81 @@ +package com.baeldung.si; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.SubscribableChannel; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.si.security.MessageConsumer; +import com.baeldung.si.security.SecuredDirectChannel; +import com.baeldung.si.security.SecurityConfig; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { SecurityConfig.class, SecuredDirectChannel.class, MessageConsumer.class }) +public class TestSpringIntegrationSecurity { + + @Autowired + SubscribableChannel startDirectChannel; + + @Autowired + MessageConsumer messageConsumer; + + final String DIRECT_CHANNEL_MESSAGE = "Direct channel message"; + + @Test(expected = AuthenticationCredentialsNotFoundException.class) + public void givenNoUser_whenSendToDirectChannel_thenCredentialNotFound() { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "jane", roles = { "LOGGER" }) + public void givenRoleLogger_whenSendMessageToDirectChannel_thenAccessDenied() throws Throwable { + try { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "jane") + public void givenJane_whenSendMessageToDirectChannel_thenAccessDenied() throws Throwable { + try { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(roles = { "VIEWER" }) + public void givenRoleViewer_whenSendToDirectChannel_thenAccessDenied() throws Throwable { + try { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test + @WithMockUser(roles = { "LOGGER", "VIEWER", "EDITOR" }) + public void givenRoleLoggerAndUser_whenSendMessageToDirectChannel_thenFlowCompletedSuccessfully() { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + assertEquals(DIRECT_CHANNEL_MESSAGE, messageConsumer.getMessageContent()); + } + + @Test + @WithMockUser(username = "jane", roles = { "LOGGER", "EDITOR" }) + public void givenJaneLoggerEditor_whenSendToDirectChannel_thenFlowCompleted() { + startDirectChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + assertEquals(DIRECT_CHANNEL_MESSAGE, messageConsumer.getMessageContent()); + } + +} diff --git a/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurityExecutor.java b/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurityExecutor.java new file mode 100644 index 0000000000..b06136a7ca --- /dev/null +++ b/spring-integration/src/test/java/com/baeldung/si/TestSpringIntegrationSecurityExecutor.java @@ -0,0 +1,68 @@ +package com.baeldung.si; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.SubscribableChannel; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.si.security.MessageConsumer; +import com.baeldung.si.security.SecurityConfig; +import com.baeldung.si.security.SecurityPubSubChannel; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { SecurityPubSubChannel.class, MessageConsumer.class, SecurityConfig.class }) +public class TestSpringIntegrationSecurityExecutor { + + @Autowired + SubscribableChannel startPSChannel; + + @Autowired + MessageConsumer messageConsumer; + + @Autowired + ThreadPoolTaskExecutor executor; + + final String DIRECT_CHANNEL_MESSAGE = "Direct channel message"; + + @Before + public void clearData() { + messageConsumer.setMessagePSContent(new ConcurrentHashMap<>()); + executor.setWaitForTasksToCompleteOnShutdown(true); + } + + @Test + @WithMockUser(username = "user", roles = { "VIEWER" }) + public void givenRoleUser_whenSendMessageToPSChannel_thenNoMessageArrived() throws IllegalStateException, InterruptedException { + startPSChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + + executor.getThreadPoolExecutor().awaitTermination(2, TimeUnit.SECONDS); + + assertEquals(1, messageConsumer.getMessagePSContent().size()); + assertTrue(messageConsumer.getMessagePSContent().values().contains("user")); + } + + @Test + @WithMockUser(username = "user", roles = { "LOGGER", "VIEWER" }) + public void givenRoleUserAndLogger_whenSendMessageToPSChannel_then2GetMessages() throws IllegalStateException, InterruptedException { + startPSChannel.send(new GenericMessage(DIRECT_CHANNEL_MESSAGE)); + + executor.getThreadPoolExecutor().awaitTermination(2, TimeUnit.SECONDS); + + assertEquals(2, messageConsumer.getMessagePSContent().size()); + assertTrue(messageConsumer.getMessagePSContent().values().contains("user")); + assertTrue(messageConsumer.getMessagePSContent().values().contains("ROLE_LOGGER,ROLE_VIEWER")); + } + +} From 5a34de448edd30514b3454dddcb30f9ce5ea749e Mon Sep 17 00:00:00 2001 From: Orry Date: Tue, 6 Feb 2018 21:47:26 +0200 Subject: [PATCH 114/118] AssertJ Exception Assertions (#3600) * Add XML, JavaConfig and Autowired examples. * BAEL-1517: Added Java7 style assertions * BAEL-1517: Upgrade AssertJ to 3.9.0; add Java 8 style assertion tests * Revert "Add XML, JavaConfig and Autowired examples." This reverts commit 8f4df6b903866dac1725832d06ee7382fc89d0ce. --- testing-modules/testing/pom.xml | 2 +- .../exceptions/Java7StyleAssertions.java | 24 +++++++++++ .../exceptions/Java8StyleAssertions.java | 42 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java7StyleAssertions.java create mode 100644 testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java8StyleAssertions.java diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index c76045380b..91792a4681 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -173,7 +173,7 @@ 0.7.7.201606060606 21.0 3.1.0 - 3.6.1 + 3.9.0 2.1.0 0.32 1.1.0 diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java7StyleAssertions.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java7StyleAssertions.java new file mode 100644 index 0000000000..07a5be1118 --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java7StyleAssertions.java @@ -0,0 +1,24 @@ +package com.baeldung.testing.assertj.exceptions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; + +import org.junit.Test; + +public class Java7StyleAssertions { + + @Test + public void whenDividingByZero_thenArithmeticException() { + try { + int numerator = 10; + int denominator = 0; + int quotient = numerator / denominator; + fail("ArithmeticException expected because dividing by zero yields an ArithmeticException."); + failBecauseExceptionWasNotThrown(ArithmeticException.class); + } catch (Exception e) { + assertThat(e).hasMessage("/ by zero"); + assertThat(e).isInstanceOf(ArithmeticException.class); + } + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java8StyleAssertions.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java8StyleAssertions.java new file mode 100644 index 0000000000..53f192bb2f --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/exceptions/Java8StyleAssertions.java @@ -0,0 +1,42 @@ +package com.baeldung.testing.assertj.exceptions; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.catchThrowable; + +import org.junit.Test; + +public class Java8StyleAssertions { + + @Test + public void whenDividingByZero_thenArithmeticException() { + assertThatThrownBy(() -> { + int numerator = 10; + int denominator = 0; + int quotient = numerator / denominator; + }).isInstanceOf(ArithmeticException.class) + .hasMessageContaining("/ by zero"); + + assertThatExceptionOfType(ArithmeticException.class).isThrownBy(() -> { + int numerator = 10; + int denominator = 0; + int quotient = numerator / denominator; + }) + .withMessageContaining("/ by zero"); + + // BDD style: + + // when + Throwable thrown = catchThrowable(() -> { + int numerator = 10; + int denominator = 0; + int quotient = numerator / denominator; + }); + + // then + assertThat(thrown).isInstanceOf(ArithmeticException.class) + .hasMessageContaining("/ by zero"); + + } +} From efb66e300108383c5c72b304dc81429dba1a7acf Mon Sep 17 00:00:00 2001 From: deep20jain Date: Wed, 7 Feb 2018 01:37:38 +0530 Subject: [PATCH 115/118] Bael 1299 - Maze Solver - deep20jain@gmail.com (#3537) * Maze solver using DFS * Adding BFS maze solver * Fixing formatting --- .../algorithms/maze/solver/BFSMazeSolver.java | 52 +++++++ .../algorithms/maze/solver/Coordinate.java | 31 ++++ .../algorithms/maze/solver/DFSMazeSolver.java | 48 ++++++ .../baeldung/algorithms/maze/solver/Maze.java | 141 ++++++++++++++++++ .../algorithms/maze/solver/MazeDriver.java | 34 +++++ algorithms/src/main/resources/maze/maze1.txt | 12 ++ algorithms/src/main/resources/maze/maze2.txt | 22 +++ 7 files changed, 340 insertions(+) create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/maze/solver/BFSMazeSolver.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Coordinate.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/maze/solver/DFSMazeSolver.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Maze.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/maze/solver/MazeDriver.java create mode 100644 algorithms/src/main/resources/maze/maze1.txt create mode 100644 algorithms/src/main/resources/maze/maze2.txt diff --git a/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/BFSMazeSolver.java b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/BFSMazeSolver.java new file mode 100644 index 0000000000..08972251b8 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/BFSMazeSolver.java @@ -0,0 +1,52 @@ +package com.baeldung.algorithms.maze.solver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class BFSMazeSolver { + private static final int[][] DIRECTIONS = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } }; + + public List solve(Maze maze) { + LinkedList nextToVisit = new LinkedList<>(); + Coordinate start = maze.getEntry(); + nextToVisit.add(start); + + while (!nextToVisit.isEmpty()) { + Coordinate cur = nextToVisit.remove(); + + if (!maze.isValidLocation(cur.getX(), cur.getY()) || maze.isExplored(cur.getX(), cur.getY())) { + continue; + } + + if (maze.isWall(cur.getX(), cur.getY())) { + maze.setVisited(cur.getX(), cur.getY(), true); + continue; + } + + if (maze.isExit(cur.getX(), cur.getY())) { + return backtrackPath(cur); + } + + for (int[] direction : DIRECTIONS) { + Coordinate coordinate = new Coordinate(cur.getX() + direction[0], cur.getY() + direction[1], cur); + nextToVisit.add(coordinate); + maze.setVisited(cur.getX(), cur.getY(), true); + } + } + return Collections.emptyList(); + } + + private List backtrackPath(Coordinate cur) { + List path = new ArrayList<>(); + Coordinate iter = cur; + + while (iter != null) { + path.add(iter); + iter = iter.parent; + } + + return path; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Coordinate.java b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Coordinate.java new file mode 100644 index 0000000000..09b2ced5e6 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Coordinate.java @@ -0,0 +1,31 @@ +package com.baeldung.algorithms.maze.solver; + +public class Coordinate { + int x; + int y; + Coordinate parent; + + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + this.parent = null; + } + + public Coordinate(int x, int y, Coordinate parent) { + this.x = x; + this.y = y; + this.parent = parent; + } + + int getX() { + return x; + } + + int getY() { + return y; + } + + Coordinate getParent() { + return parent; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/DFSMazeSolver.java b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/DFSMazeSolver.java new file mode 100644 index 0000000000..f9640066b9 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/DFSMazeSolver.java @@ -0,0 +1,48 @@ +package com.baeldung.algorithms.maze.solver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DFSMazeSolver { + private static final int[][] DIRECTIONS = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } }; + + public List solve(Maze maze) { + List path = new ArrayList<>(); + if (explore(maze, maze.getEntry() + .getX(), + maze.getEntry() + .getY(), + path)) { + return path; + } + return Collections.emptyList(); + } + + private boolean explore(Maze maze, int row, int col, List path) { + if (!maze.isValidLocation(row, col) || maze.isWall(row, col) || maze.isExplored(row, col)) { + return false; + } + + path.add(new Coordinate(row, col)); + maze.setVisited(row, col, true); + + if (maze.isExit(row, col)) { + return true; + } + + for (int[] direction : DIRECTIONS) { + Coordinate coordinate = getNextCoordinate(row, col, direction[0], direction[1]); + if (explore(maze, coordinate.getX(), coordinate.getY(), path)) { + return true; + } + } + + path.remove(path.size() - 1); + return false; + } + + private Coordinate getNextCoordinate(int row, int col, int i, int j) { + return new Coordinate(row + i, col + j); + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Maze.java b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Maze.java new file mode 100644 index 0000000000..8aaa44d9b1 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/Maze.java @@ -0,0 +1,141 @@ +package com.baeldung.algorithms.maze.solver; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class Maze { + private static final int ROAD = 0; + private static final int WALL = 1; + private static final int START = 2; + private static final int EXIT = 3; + private static final int PATH = 4; + + private int[][] maze; + private boolean[][] visited; + private Coordinate start; + private Coordinate end; + + public Maze(File maze) throws FileNotFoundException { + String fileText = ""; + try (Scanner input = new Scanner(maze)) { + while (input.hasNextLine()) { + fileText += input.nextLine() + "\n"; + } + } + initializeMaze(fileText); + } + + private void initializeMaze(String text) { + if (text == null || (text = text.trim()).length() == 0) { + throw new IllegalArgumentException("empty lines data"); + } + + String[] lines = text.split("[\r]?\n"); + maze = new int[lines.length][lines[0].length()]; + visited = new boolean[lines.length][lines[0].length()]; + + for (int row = 0; row < getHeight(); row++) { + if (lines[row].length() != getWidth()) { + throw new IllegalArgumentException("line " + (row + 1) + " wrong length (was " + lines[row].length() + " but should be " + getWidth() + ")"); + } + + for (int col = 0; col < getWidth(); col++) { + if (lines[row].charAt(col) == '#') + maze[row][col] = WALL; + else if (lines[row].charAt(col) == 'S') { + maze[row][col] = START; + start = new Coordinate(row, col); + } else if (lines[row].charAt(col) == 'E') { + maze[row][col] = EXIT; + end = new Coordinate(row, col); + } else + maze[row][col] = ROAD; + } + } + } + + public int getHeight() { + return maze.length; + } + + public int getWidth() { + return maze[0].length; + } + + public Coordinate getEntry() { + return start; + } + + public Coordinate getExit() { + return end; + } + + public boolean isExit(int x, int y) { + return x == end.getX() && y == end.getY(); + } + + public boolean isStart(int x, int y) { + return x == start.getX() && y == start.getY(); + } + + public boolean isExplored(int row, int col) { + return visited[row][col]; + } + + public boolean isWall(int row, int col) { + return maze[row][col] == WALL; + } + + public void setVisited(int row, int col, boolean value) { + visited[row][col] = value; + } + + public boolean isValidLocation(int row, int col) { + if (row < 0 || row >= getHeight() || col < 0 || col >= getWidth()) { + return false; + } + return true; + } + + public void printPath(List path) { + int[][] tempMaze = Arrays.stream(maze) + .map(int[]::clone) + .toArray(int[][]::new); + for (Coordinate coordinate : path) { + if (isStart(coordinate.getX(), coordinate.getY()) || isExit(coordinate.getX(), coordinate.getY())) { + continue; + } + tempMaze[coordinate.getX()][coordinate.getY()] = PATH; + } + System.out.println(toString(tempMaze)); + } + + public String toString(int[][] maze) { + StringBuilder result = new StringBuilder(getWidth() * (getHeight() + 1)); + for (int row = 0; row < getHeight(); row++) { + for (int col = 0; col < getWidth(); col++) { + if (maze[row][col] == ROAD) { + result.append(' '); + } else if (maze[row][col] == WALL) { + result.append('#'); + } else if (maze[row][col] == START) { + result.append('S'); + } else if (maze[row][col] == EXIT) { + result.append('E'); + } else { + result.append('.'); + } + } + result.append('\n'); + } + return result.toString(); + } + + public void reset() { + for (int i = 0; i < visited.length; i++) + Arrays.fill(visited[i], false); + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/MazeDriver.java b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/MazeDriver.java new file mode 100644 index 0000000000..60263deba3 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/maze/solver/MazeDriver.java @@ -0,0 +1,34 @@ +package com.baeldung.algorithms.maze.solver; + +import java.io.File; +import java.util.List; + +public class MazeDriver { + public static void main(String[] args) throws Exception { + File maze1 = new File("src/main/resources/maze/maze1.txt"); + File maze2 = new File("src/main/resources/maze/maze2.txt"); + + execute(maze1); + execute(maze2); + } + + private static void execute(File file) throws Exception { + Maze maze = new Maze(file); + dfs(maze); + bfs(maze); + } + + private static void bfs(Maze maze) { + BFSMazeSolver bfs = new BFSMazeSolver(); + List path = bfs.solve(maze); + maze.printPath(path); + maze.reset(); + } + + private static void dfs(Maze maze) { + DFSMazeSolver dfs = new DFSMazeSolver(); + List path = dfs.solve(maze); + maze.printPath(path); + maze.reset(); + } +} diff --git a/algorithms/src/main/resources/maze/maze1.txt b/algorithms/src/main/resources/maze/maze1.txt new file mode 100644 index 0000000000..0a6309d25b --- /dev/null +++ b/algorithms/src/main/resources/maze/maze1.txt @@ -0,0 +1,12 @@ +S ######## +# # +# ### ## # +# # # # +# # # # # +# ## ##### +# # # +# # # # # +##### #### +# # E +# # # # +########## \ No newline at end of file diff --git a/algorithms/src/main/resources/maze/maze2.txt b/algorithms/src/main/resources/maze/maze2.txt new file mode 100644 index 0000000000..22e6d0382a --- /dev/null +++ b/algorithms/src/main/resources/maze/maze2.txt @@ -0,0 +1,22 @@ +S ########################## +# # # # +# # #### ############### # +# # # # # # +# # #### # # ############### +# # # # # # # +# # # #### ### ########### # +# # # # # # +# ################## # +######### # # # # # +# # #### # ####### # # +# # ### ### # # # # # +# # ## # ##### # # +##### ####### # # # # # +# # ## ## #### # # +# ##### ####### # # +# # ############ +####### ######### # # +# # ######## # +# ####### ###### ## # E +# # # ## # +############################ \ No newline at end of file From 596efe3042c68eb91cc048356ab71a9ae95ce1d7 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Wed, 7 Feb 2018 19:45:04 +0530 Subject: [PATCH 116/118] Changes for BAEL-1472 --- persistence-modules/spring-data-redis/pom.xml | 12 ++++- .../spring/data/redis/config/RedisConfig.java | 9 ++-- .../spring/data/redis/model/Student.java | 3 ++ .../data/redis/repo/StudentRepository.java | 19 ++----- .../redis/repo/StudentRepositoryImpl.java | 49 ------------------- .../StudentRepositoryIntegrationTest.java | 40 ++++++++------- 6 files changed, 46 insertions(+), 86 deletions(-) delete mode 100644 persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepositoryImpl.java diff --git a/persistence-modules/spring-data-redis/pom.xml b/persistence-modules/spring-data-redis/pom.xml index 6cb49f11cf..0dc51e790e 100644 --- a/persistence-modules/spring-data-redis/pom.xml +++ b/persistence-modules/spring-data-redis/pom.xml @@ -16,11 +16,13 @@ UTF-8 - 4.3.7.RELEASE - 1.8.1.RELEASE + 5.0.3.RELEASE + 2.0.3.RELEASE 3.2.4 2.9.0 0.10.0 + 2.0.3.RELEASE + @@ -73,6 +75,12 @@ nosqlunit-redis ${nosqlunit.version} + + + org.springframework.data + spring-data-commons + 2.0.3.RELEASE + diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java index 4fd83a2bb6..4ea8bb4bc0 100644 --- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java +++ b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java @@ -1,8 +1,5 @@ package com.baeldung.spring.data.redis.config; -import com.baeldung.spring.data.redis.queue.MessagePublisher; -import com.baeldung.spring.data.redis.queue.RedisMessagePublisher; -import com.baeldung.spring.data.redis.queue.RedisMessageSubscriber; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -11,10 +8,16 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.redis.serializer.GenericToStringSerializer; +import com.baeldung.spring.data.redis.queue.MessagePublisher; +import com.baeldung.spring.data.redis.queue.RedisMessagePublisher; +import com.baeldung.spring.data.redis.queue.RedisMessageSubscriber; + @Configuration @ComponentScan("com.baeldung.spring.data.redis") +@EnableRedisRepositories(basePackages = "com.baeldung.spring.data.redis.repo") public class RedisConfig { @Bean diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/model/Student.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/model/Student.java index 10ba0f5ef4..b97ed23387 100644 --- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/model/Student.java +++ b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/model/Student.java @@ -2,6 +2,9 @@ package com.baeldung.spring.data.redis.model; import java.io.Serializable; +import org.springframework.data.redis.core.RedisHash; + +@RedisHash("Student") public class Student implements Serializable { public enum Gender { diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepository.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepository.java index 250c227f00..39f13bb6a7 100644 --- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepository.java +++ b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepository.java @@ -1,18 +1,9 @@ package com.baeldung.spring.data.redis.repo; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + import com.baeldung.spring.data.redis.model.Student; -import java.util.Map; - -public interface StudentRepository { - - void saveStudent(Student person); - - void updateStudent(Student student); - - Student findStudent(String id); - - Map findAllStudents(); - - void deleteStudent(String id); -} +@Repository +public interface StudentRepository extends CrudRepository {} diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepositoryImpl.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepositoryImpl.java deleted file mode 100644 index f13bef0f54..0000000000 --- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/repo/StudentRepositoryImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.baeldung.spring.data.redis.repo; - -import com.baeldung.spring.data.redis.model.Student; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.PostConstruct; -import java.util.Map; - -@Repository -public class StudentRepositoryImpl implements StudentRepository { - - private static final String KEY = "Student"; - - private RedisTemplate redisTemplate; - private HashOperations hashOperations; - - @Autowired - public StudentRepositoryImpl(RedisTemplate redisTemplate) { - this.redisTemplate = redisTemplate; - } - - @PostConstruct - private void init() { - hashOperations = redisTemplate.opsForHash(); - } - - public void saveStudent(final Student student) { - hashOperations.put(KEY, student.getId(), student); - } - - public void updateStudent(final Student student) { - hashOperations.put(KEY, student.getId(), student); - } - - public Student findStudent(final String id) { - return (Student) hashOperations.get(KEY, id); - } - - public Map findAllStudents() { - return hashOperations.entries(KEY); - } - - public void deleteStudent(final String id) { - hashOperations.delete(KEY, id); - } -} diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java index 1028c0bc24..66ef3c21b2 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java @@ -1,17 +1,20 @@ package com.baeldung.spring.data.redis.repo; -import com.baeldung.spring.data.redis.config.RedisConfig; -import com.baeldung.spring.data.redis.model.Student; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import com.baeldung.spring.data.redis.config.RedisConfig; +import com.baeldung.spring.data.redis.model.Student; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = RedisConfig.class) @@ -23,18 +26,18 @@ public class StudentRepositoryIntegrationTest { @Test public void whenSavingStudent_thenAvailableOnRetrieval() throws Exception { final Student student = new Student("Eng2015001", "John Doe", Student.Gender.MALE, 1); - studentRepository.saveStudent(student); - final Student retrievedStudent = studentRepository.findStudent(student.getId()); + studentRepository.save(student); + final Student retrievedStudent = studentRepository.findById(student.getId()).get(); assertEquals(student.getId(), retrievedStudent.getId()); } @Test public void whenUpdatingStudent_thenAvailableOnRetrieval() throws Exception { final Student student = new Student("Eng2015001", "John Doe", Student.Gender.MALE, 1); - studentRepository.saveStudent(student); + studentRepository.save(student); student.setName("Richard Watson"); - studentRepository.saveStudent(student); - final Student retrievedStudent = studentRepository.findStudent(student.getId()); + studentRepository.save(student); + final Student retrievedStudent = studentRepository.findById(student.getId()).get(); assertEquals(student.getName(), retrievedStudent.getName()); } @@ -42,18 +45,19 @@ public class StudentRepositoryIntegrationTest { public void whenSavingStudents_thenAllShouldAvailableOnRetrieval() throws Exception { final Student engStudent = new Student("Eng2015001", "John Doe", Student.Gender.MALE, 1); final Student medStudent = new Student("Med2015001", "Gareth Houston", Student.Gender.MALE, 2); - studentRepository.saveStudent(engStudent); - studentRepository.saveStudent(medStudent); - final Map retrievedStudent = studentRepository.findAllStudents(); - assertEquals(retrievedStudent.size(), 2); + studentRepository.save(engStudent); + studentRepository.save(medStudent); + List students = new ArrayList<>(); + studentRepository.findAll().forEach(students::add); + assertEquals(students.size(), 2); } @Test public void whenDeletingStudent_thenNotAvailableOnRetrieval() throws Exception { final Student student = new Student("Eng2015001", "John Doe", Student.Gender.MALE, 1); - studentRepository.saveStudent(student); - studentRepository.deleteStudent(student.getId()); - final Student retrievedStudent = studentRepository.findStudent(student.getId()); + studentRepository.save(student); + studentRepository.deleteById(student.getId()); + final Student retrievedStudent = studentRepository.findById(student.getId()).orElse(null); assertNull(retrievedStudent); } } \ No newline at end of file From b2372b50061766aa27e203d6a1f84a2e04fb4198 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Wed, 7 Feb 2018 19:46:42 +0530 Subject: [PATCH 117/118] Changes for BAEL-1472 --- persistence-modules/spring-data-redis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence-modules/spring-data-redis/pom.xml b/persistence-modules/spring-data-redis/pom.xml index 0dc51e790e..0b9075147d 100644 --- a/persistence-modules/spring-data-redis/pom.xml +++ b/persistence-modules/spring-data-redis/pom.xml @@ -79,7 +79,7 @@ org.springframework.data spring-data-commons - 2.0.3.RELEASE + ${spring-data-commons.version} From 0743dec07c59c0fecb8ac3ad0052457d5e6feaf6 Mon Sep 17 00:00:00 2001 From: iaforek Date: Thu, 8 Feb 2018 06:26:42 +0000 Subject: [PATCH 118/118] Extracted 'constraints' methods and renamed variables (#3580) * Code for Dependency Injection Article. * Added Java based configuration. Downloaded formatter.xml and reformatted all changed files. Manually changed tab into 4 spaces in XML configuration files. * BAEL-434 - Spring Roo project files generated by Spring Roo. No formatting applied. Added POM, java and resources folders. * Moved project from roo to spring-roo folder. * BAEL-838 Initial code showing how to remove last char - helper class and tests. * BAEL-838 Corrected Helper class and associated empty string test case. Added StringUtils.substing tests. * BAEL-838 Refromatted code using formatter.xml. Added Assert.assertEquals import. Renamed test to follow convention. Reordered tests. * BAEL-838 - Added regex method and updated tests. * BAEL-838 Added new line examples. * BAEL-838 Renamed RemoveLastChar class to StringHelper and added Java8 examples. Refactord code. * BAEL-838 Changed method names * BAEL-838 Tiny change to keep code consistant. Return null or empty. * BAEL-838 Removed unresolved conflict. * BAEL-821 New class that shows different rounding techniques. Updated POM. * BAEL-821 - Added unit test for different round methods. * BAEL-821 Changed test method name to follow the convention * BAEL-821 Added more test and updated round methods. * BAEL-837 - initial commit. A few examples of adding doubles. * BAEL-837 - Couple of smaller changes * BAEL-837 - Added jUnit test. * BAEL-579 Updated Spring Cloud Version I was getting error: java.lang.NoSuchMethodError: org.springframework.cloud.config.environment.Environment After version update, all is okay. * BAEL-579 Added actuator to Cloud Config Client. * BAEL-579 Enabled cloud bus and updated dependencies. * BAEL-579 Config Client using Spring Cloud Bus. * BAEL-579 Recreated Basic Config Server. * BAEL-579 Recreated Config Client. * BAEL-579 Removed test Git URL. * BAEL-579 Added Actuator to Config Client * BAEL-579 Added Spring Cloud Bus to Client. * BAEL-579 Server changes for Spring Cloud Bus Added dependencies and removed git.clone-on-start as this was causing server to throw errors after git properties change. * BAEL-579 Removed Git URL. * Revert "BAEL-579 Updated Spring Cloud Version" This reverts commit f775bf91e53a1ecfb9b70596688d7c8202bf495f. * Revert "BAEL-579 Config Client using Spring Cloud Bus." This reverts commit 1d96bc5761994a33af9a7a9aa5ab68604a5b44dc. * Revert "BAEL-579 Enabled cloud bus and updated dependencies." This reverts commit 7845da922d89d53506dd0fff387ea13694c50bc1. * Revert "BAEL-579 Added actuator to Cloud Config Client." This reverts commit 076657a26a57e0aa676989a4d97966a3b9d53e1c. * BAEL-579 Added missing dependency versions. * BAEL-579 Added missing dependency versions. * Updated gitignore * BAEL-1065 Simple performance check StringBuffer vs StringBuilder. * BAEL-1065 Added JMH benchmarks * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Dancing Links Algorithm. Smaller changes to Backtracking * BAEL-1298 Resolve conflict - use most up-to-date POM * Updated code - mostly with CONSTANTS. Extracted methods. * Removed pointless Java8 code. Renamed constant * Extracted 'constraints' methods and renamed coverBoard variable * Extracted 'constraints' methods and renamed coverBoard variable --- .../sudoku/BacktrackingAlgorithm.java | 2 +- .../algorithms/sudoku/DancingLinks.java | 4 +- .../sudoku/DancingLinksAlgorithm.java | 87 +++++++++++-------- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java index dc2a324c12..ff426cbe68 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java @@ -101,4 +101,4 @@ public class BacktrackingAlgorithm { } return true; } -} \ No newline at end of file +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java index e5a02b7c91..d3cbb2bd02 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java @@ -120,10 +120,10 @@ public class DancingLinks { } private static void printSolution(int[][] result) { - int N = result.length; + int size = result.length; for (int[] aResult : result) { StringBuilder ret = new StringBuilder(); - for (int j = 0; j < N; j++) { + for (int j = 0; j < size; j++) { ret.append(aResult[j]).append(" "); } System.out.println(ret); diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java index 6b0f57a075..76b686afa6 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java @@ -39,70 +39,83 @@ public class DancingLinksAlgorithm { } private boolean[][] createExactCoverBoard() { - boolean[][] R = new boolean[BOARD_SIZE * BOARD_SIZE * MAX_VALUE][BOARD_SIZE * BOARD_SIZE * CONSTRAINTS]; + boolean[][] coverBoard = new boolean[BOARD_SIZE * BOARD_SIZE * MAX_VALUE][BOARD_SIZE * BOARD_SIZE * CONSTRAINTS]; int hBase = 0; + hBase = checkCellConstraint(coverBoard, hBase); + hBase = checkRowConstraint(coverBoard, hBase); + hBase = checkColumnConstraint(coverBoard, hBase); + checkSubsectionConstraint(coverBoard, hBase); + + return coverBoard; + } - // Cell constraint. - for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { - for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++, hBase++) { - for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++) { - int index = getIndex(r, c, n); - R[index][hBase] = true; - } - } - } - - // Row constrain. - for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { - for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { - for (int c1 = COVER_START_INDEX; c1 <= BOARD_SIZE; c1++) { - int index = getIndex(r, c1, n); - R[index][hBase] = true; - } - } - } - - // Column constraint. - for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++) { - for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { - for (int r1 = COVER_START_INDEX; r1 <= BOARD_SIZE; r1++) { - int index = getIndex(r1, c, n); - R[index][hBase] = true; - } - } - } - - // Subsection constraint + private int checkSubsectionConstraint(boolean[][] coverBoard, int hBase) { for (int br = COVER_START_INDEX; br <= BOARD_SIZE; br += SUBSECTION_SIZE) { for (int bc = COVER_START_INDEX; bc <= BOARD_SIZE; bc += SUBSECTION_SIZE) { for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { for (int rDelta = 0; rDelta < SUBSECTION_SIZE; rDelta++) { for (int cDelta = 0; cDelta < SUBSECTION_SIZE; cDelta++) { int index = getIndex(br + rDelta, bc + cDelta, n); - R[index][hBase] = true; + coverBoard[index][hBase] = true; } } } } } - return R; + return hBase; + } + + private int checkColumnConstraint(boolean[][] coverBoard, int hBase) { + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int r1 = COVER_START_INDEX; r1 <= BOARD_SIZE; r1++) { + int index = getIndex(r1, c, n); + coverBoard[index][hBase] = true; + } + } + } + return hBase; + } + + private int checkRowConstraint(boolean[][] coverBoard, int hBase) { + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int c1 = COVER_START_INDEX; c1 <= BOARD_SIZE; c1++) { + int index = getIndex(r, c1, n); + coverBoard[index][hBase] = true; + } + } + } + return hBase; + } + + private int checkCellConstraint(boolean[][] coverBoard, int hBase) { + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++, hBase++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++) { + int index = getIndex(r, c, n); + coverBoard[index][hBase] = true; + } + } + } + return hBase; } private boolean[][] initializeExactCoverBoard(int[][] board) { - boolean[][] R = createExactCoverBoard(); + boolean[][] coverBoard = createExactCoverBoard(); for (int i = COVER_START_INDEX; i <= BOARD_SIZE; i++) { for (int j = COVER_START_INDEX; j <= BOARD_SIZE; j++) { int n = board[i - 1][j - 1]; if (n != NO_VALUE) { for (int num = MIN_VALUE; num <= MAX_VALUE; num++) { if (num != n) { - Arrays.fill(R[getIndex(i, j, num)], false); + Arrays.fill(coverBoard[getIndex(i, j, num)], false); } } } } } - return R; + return coverBoard; } } \ No newline at end of file