Merge branch 'eugenp:master' into master
This commit is contained in:
commit
3b043a30dc
|
@ -0,0 +1,43 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
/**
|
||||
* This class shows examples of how you should use the lock
|
||||
*
|
||||
*/
|
||||
public class ExampleUsage {
|
||||
|
||||
void doWithSimpleExclusiveLock(String key) {
|
||||
SimpleExclusiveLockByKey simpleExclusiveLockByKey = new SimpleExclusiveLockByKey();
|
||||
if (simpleExclusiveLockByKey.tryLock(key)) {
|
||||
try {
|
||||
// do stuff
|
||||
} finally {
|
||||
// it is very important to unlock in the finally block to avoid locking keys forever
|
||||
simpleExclusiveLockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A concrete example can be found in the unit tests
|
||||
void doWithLock(String key) {
|
||||
LockByKey lockByKey = new LockByKey();
|
||||
lockByKey.lock(key);
|
||||
try {
|
||||
// do stuff
|
||||
} finally {
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
// It works exactly the same as with locks
|
||||
void doWithSemaphore(String key) {
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
try {
|
||||
// do stuff
|
||||
} finally {
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class LockByKey {
|
||||
|
||||
private static class LockWrapper {
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final AtomicInteger numberOfThreadsInQueue = new AtomicInteger(1);
|
||||
|
||||
private LockWrapper addThreadInQueue() {
|
||||
numberOfThreadsInQueue.incrementAndGet();
|
||||
return this;
|
||||
}
|
||||
|
||||
private int removeThreadFromQueue() {
|
||||
return numberOfThreadsInQueue.decrementAndGet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<String, LockWrapper> locks = new ConcurrentHashMap<String, LockWrapper>();
|
||||
|
||||
public void lock(String key) {
|
||||
LockWrapper lockWrapper = locks.compute(key, (k, v) -> v == null ? new LockWrapper() : v.addThreadInQueue());
|
||||
lockWrapper.lock.lock();
|
||||
}
|
||||
|
||||
public void unlock(String key) {
|
||||
LockWrapper lockWrapper = locks.get(key);
|
||||
lockWrapper.lock.unlock();
|
||||
if (lockWrapper.removeThreadFromQueue() == 0) {
|
||||
// NB : We pass in the specific value to remove to handle the case where another thread would queue right before the removal
|
||||
locks.remove(key, lockWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SimpleExclusiveLockByKey {
|
||||
|
||||
private static Set<String> usedKeys= ConcurrentHashMap.newKeySet();
|
||||
|
||||
public boolean tryLock(String key) {
|
||||
return usedKeys.add(key);
|
||||
}
|
||||
|
||||
public void unlock(String key) {
|
||||
usedKeys.remove(key);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class SimultaneousEntriesLockByKey {
|
||||
|
||||
private static final int ALLOWED_THREADS = 2;
|
||||
|
||||
private static ConcurrentHashMap<String, Semaphore> semaphores = new ConcurrentHashMap<String, Semaphore>();
|
||||
|
||||
public void lock(String key) {
|
||||
Semaphore semaphore = semaphores.compute(key, (k, v) -> v == null ? new Semaphore(ALLOWED_THREADS) : v);
|
||||
semaphore.acquireUninterruptibly();
|
||||
}
|
||||
|
||||
public void unlock(String key) {
|
||||
Semaphore semaphore = semaphores.get(key);
|
||||
semaphore.release();
|
||||
if (semaphore.availablePermits() == ALLOWED_THREADS) {
|
||||
semaphores.remove(key, semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LockByKeyUnitTest {
|
||||
|
||||
@Test
|
||||
void givenNoLockedKey_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
AtomicBoolean threadWasExecuted = new AtomicBoolean(false);
|
||||
Thread thread = new Thread(() -> {
|
||||
String key = "key";
|
||||
LockByKey lockByKey = new LockByKey();
|
||||
lockByKey.lock(key);
|
||||
try {
|
||||
threadWasExecuted.set(true);
|
||||
} finally {
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
thread.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(threadWasExecuted.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenLockedKey_WhenLock_ThenFailure() throws InterruptedException {
|
||||
String key = "key";
|
||||
LockByKey lockByKey = new LockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
LockByKey otherLockByKey = new LockByKey();
|
||||
otherLockByKey.lock(key);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertFalse(anotherThreadWasExecuted.get());
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnotherKeyLocked_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
String key = "key";
|
||||
LockByKey lockByKey = new LockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
String anotherKey = "anotherKey";
|
||||
LockByKey otherLockByKey = new LockByKey();
|
||||
otherLockByKey.lock(anotherKey);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(anotherKey);
|
||||
}
|
||||
});
|
||||
try {
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(anotherThreadWasExecuted.get());
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUnlockedKey_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
String key = "key";
|
||||
LockByKey lockByKey = new LockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
LockByKey otherLockByKey = new LockByKey();
|
||||
otherLockByKey.lock(key);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
lockByKey.unlock(key);
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(anotherThreadWasExecuted.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SimpleExclusiveLockByKeyUnitTest {
|
||||
|
||||
@BeforeEach
|
||||
void cleanUpLocks() throws Exception {
|
||||
Field field = SimpleExclusiveLockByKey.class.getDeclaredField("usedKeys");
|
||||
field.setAccessible(true);
|
||||
field.set(null, ConcurrentHashMap.newKeySet());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNoLockedKey_WhenTryLock_ThenSuccess() {
|
||||
SimpleExclusiveLockByKey lockByKey = new SimpleExclusiveLockByKey();
|
||||
assertTrue(lockByKey.tryLock("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenLockedKey_WhenTryLock_ThenFailure() {
|
||||
String key = "key";
|
||||
SimpleExclusiveLockByKey lockByKey = new SimpleExclusiveLockByKey();
|
||||
lockByKey.tryLock(key);
|
||||
assertFalse(lockByKey.tryLock(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnotherKeyLocked_WhenTryLock_ThenSuccess() {
|
||||
SimpleExclusiveLockByKey lockByKey = new SimpleExclusiveLockByKey();
|
||||
lockByKey.tryLock("other");
|
||||
assertTrue(lockByKey.tryLock("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUnlockedKey_WhenTryLock_ThenSuccess() {
|
||||
String key = "key";
|
||||
SimpleExclusiveLockByKey lockByKey = new SimpleExclusiveLockByKey();
|
||||
lockByKey.tryLock(key);
|
||||
lockByKey.unlock(key);
|
||||
assertTrue(lockByKey.tryLock(key));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package com.baeldung.lockbykey;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SimultaneousEntriesLockByKeyUnitTest {
|
||||
|
||||
@Test
|
||||
void givenNoKeyUsed_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
AtomicBoolean threadWasExecuted = new AtomicBoolean(false);
|
||||
Thread thread = new Thread(() -> {
|
||||
String key = "key";
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
try {
|
||||
threadWasExecuted.set(true);
|
||||
} finally {
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
thread.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(threadWasExecuted.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenKeyLockedWithRemainingPermits_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
String key = "key";
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
SimultaneousEntriesLockByKey otherLockByKeyWithSemaphore = new SimultaneousEntriesLockByKey();
|
||||
otherLockByKeyWithSemaphore.lock(key);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKeyWithSemaphore.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(anotherThreadWasExecuted.get());
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenKeyLockedWithNoRemainingPermits_WhenLock_ThenFailure() throws InterruptedException {
|
||||
String key = "key";
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey1 = new Thread(() -> {
|
||||
SimultaneousEntriesLockByKey otherLockByKeyWithSemaphore = new SimultaneousEntriesLockByKey();
|
||||
otherLockByKeyWithSemaphore.lock(key);
|
||||
try {
|
||||
Thread.sleep(200); // make sure this thread will release the lock after the assertion
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
} finally {
|
||||
otherLockByKeyWithSemaphore.unlock(key);
|
||||
}
|
||||
});
|
||||
Thread threadLockingOnAnotherKey2 = new Thread(() -> {
|
||||
SimultaneousEntriesLockByKey otherLockByKey = new SimultaneousEntriesLockByKey();
|
||||
try {
|
||||
Thread.sleep(50); // make sure thread1 will acquire the key first
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
otherLockByKey.lock(key);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
threadLockingOnAnotherKey1.start();
|
||||
threadLockingOnAnotherKey2.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertFalse(anotherThreadWasExecuted.get());
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnotherKeyLocked_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
String key = "key";
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
String anotherKey = "anotherKey";
|
||||
SimultaneousEntriesLockByKey otherLockByKey = new SimultaneousEntriesLockByKey();
|
||||
otherLockByKey.lock(anotherKey);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(anotherKey);
|
||||
}
|
||||
});
|
||||
try {
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(anotherThreadWasExecuted.get());
|
||||
lockByKey.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUnlockedKey_WhenLock_ThenSuccess() throws InterruptedException {
|
||||
String key = "key";
|
||||
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey();
|
||||
lockByKey.lock(key);
|
||||
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
|
||||
Thread threadLockingOnAnotherKey = new Thread(() -> {
|
||||
SimultaneousEntriesLockByKey otherLockByKey = new SimultaneousEntriesLockByKey();
|
||||
otherLockByKey.lock(key);
|
||||
try {
|
||||
anotherThreadWasExecuted.set(true);
|
||||
} finally {
|
||||
otherLockByKey.unlock(key);
|
||||
}
|
||||
});
|
||||
try {
|
||||
lockByKey.unlock(key);
|
||||
threadLockingOnAnotherKey.start();
|
||||
Thread.sleep(100);
|
||||
} finally {
|
||||
assertTrue(anotherThreadWasExecuted.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
<module>maven-copy-files</module>
|
||||
<module>maven-custom-plugin</module>
|
||||
<module>maven-exec-plugin</module>
|
||||
<!-- <module>maven-generate-war</module> --> <!-- We haven't upgraded to java 11. -->
|
||||
<module>maven-integration-test</module>
|
||||
<module>maven-multi-source</module>
|
||||
<module>maven-plugins</module>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>hibernate-mapping-2y</artifactId>
|
||||
<artifactId>hibernate-mapping-2</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<name>hibernate-mapping-2</name>
|
||||
|
||||
|
|
|
@ -45,6 +45,13 @@ public class HibernateManyToManyAnnotationJavaConfigMainIntegrationTest {
|
|||
projects.add(new Project("Networking Project"));
|
||||
session.persist(new Employee("Peter", "Oven", projects));
|
||||
session.persist(new Employee("Allan", "Norman", projects));
|
||||
|
||||
Set<Employee> employees = new HashSet<Employee>();
|
||||
employees.add(new Employee("Sam", "Curran"));
|
||||
employees.add(new Employee("Tom", "Curran"));
|
||||
Project project = new Project("Database Project");
|
||||
project.setEmployees(employees);
|
||||
session.persist(project);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.baeldung.hibernate.manytomany;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -23,60 +23,73 @@ import com.baeldung.manytomany.util.HibernateUtil;
|
|||
* Configured in: manytomany.cfg.xml
|
||||
*/
|
||||
public class HibernateManyToManyAnnotationMainIntegrationTest {
|
||||
private static SessionFactory sessionFactory;
|
||||
private static SessionFactory sessionFactory;
|
||||
|
||||
private Session session;
|
||||
private Session session;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeTests() {
|
||||
sessionFactory = HibernateUtil.getSessionFactory();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void beforeTests() {
|
||||
sessionFactory = HibernateUtil.getSessionFactory();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
session = sessionFactory.openSession();
|
||||
session.beginTransaction();
|
||||
}
|
||||
@Before
|
||||
public void setUp() {
|
||||
session = sessionFactory.openSession();
|
||||
session.beginTransaction();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenData_whenInsert_thenCreatesMtoMrelationship() {
|
||||
String[] employeeData = { "Peter Oven", "Allan Norman" };
|
||||
String[] projectData = { "IT Project", "Networking Project" };
|
||||
Set<Project> projects = new HashSet<Project>();
|
||||
|
||||
for (String proj : projectData) {
|
||||
projects.add(new Project(proj));
|
||||
}
|
||||
|
||||
for (String emp : employeeData) {
|
||||
Employee employee = new Employee(emp.split(" ")[0], emp.split(" ")[1]);
|
||||
assertEquals(0, employee.getProjects().size());
|
||||
employee.setProjects(projects);
|
||||
session.persist(employee);
|
||||
assertNotNull(employee);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void givenSession_whenRead_thenReturnsMtoMdata() {
|
||||
prepareData();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Employee> employeeList = session.createQuery("FROM Employee").list();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Employee> employeeList = session.createQuery("FROM Employee").list();
|
||||
List<Project> projectList = session.createQuery("FROM Project").list();
|
||||
assertNotNull(employeeList);
|
||||
assertNotNull(projectList);
|
||||
assertEquals(2, employeeList.size());
|
||||
assertEquals(2, projectList.size());
|
||||
|
||||
for(Employee employee : employeeList) {
|
||||
assertNotNull(employee.getProjects());
|
||||
assertEquals(2, employee.getProjects().size());
|
||||
}
|
||||
for(Project project : projectList) {
|
||||
assertNotNull(project.getEmployees());
|
||||
assertEquals(2, project.getEmployees().size());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
session.getTransaction()
|
||||
.commit();
|
||||
session.close();
|
||||
}
|
||||
private void prepareData() {
|
||||
String[] employeeData = { "Peter Oven", "Allan Norman" };
|
||||
String[] projectData = { "IT Project", "Networking Project" };
|
||||
Set<Project> projects = new HashSet<Project>();
|
||||
|
||||
@AfterClass
|
||||
public static void afterTests() {
|
||||
sessionFactory.close();
|
||||
}
|
||||
for (String proj : projectData) {
|
||||
projects.add(new Project(proj));
|
||||
}
|
||||
|
||||
for (String emp : employeeData) {
|
||||
Employee employee = new Employee(emp.split(" ")[0], emp.split(" ")[1]);
|
||||
employee.setProjects(projects);
|
||||
|
||||
for (Project proj : projects) {
|
||||
proj.getEmployees().add(employee);
|
||||
}
|
||||
|
||||
session.persist(employee);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterTests() {
|
||||
sessionFactory.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package com.baeldung.mongo;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Updates;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
public class PushOperations {
|
||||
|
||||
private static MongoClient mongoClient;
|
||||
private static String testCollectionName;
|
||||
private static String databaseName;
|
||||
|
||||
public static void setUp() {
|
||||
if (mongoClient == null) {
|
||||
mongoClient = new MongoClient("localhost", 27017);
|
||||
}
|
||||
|
||||
databaseName = "baeldung";
|
||||
testCollectionName = "orders";
|
||||
|
||||
}
|
||||
|
||||
public static void pushOperationUsingDBObject() {
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||
MongoCollection<Document> collection = database.getCollection(testCollectionName);
|
||||
DBObject listItem = new BasicDBObject("items", new BasicDBObject("itemName", "PIZZA MANIA").append("quantity", 1)
|
||||
.append("price", 800));
|
||||
BasicDBObject searchFilter = new BasicDBObject("customerId", 1023);
|
||||
BasicDBObject updateQuery = new BasicDBObject();
|
||||
updateQuery.append("$push", listItem);
|
||||
UpdateResult updateResult = collection.updateOne(searchFilter, updateQuery);
|
||||
|
||||
System.out.println("updateResult:- " + updateResult);
|
||||
}
|
||||
|
||||
public static void pushOperationUsingDocument() {
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||
MongoCollection<Document> collection = database.getCollection(testCollectionName);
|
||||
|
||||
Document item = new Document().append("itemName", "PIZZA MANIA")
|
||||
.append("quantity", 1)
|
||||
.append("price", 800);
|
||||
UpdateResult updateResult = collection.updateOne(Filters.eq("customerId", 1023), Updates.push("items", item));
|
||||
|
||||
System.out.println("updateResult:- " + updateResult);
|
||||
}
|
||||
|
||||
public static void addToSetOperation() {
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||
MongoCollection<Document> collection = database.getCollection(testCollectionName);
|
||||
|
||||
Document item = new Document().append("itemName", "PIZZA MANIA")
|
||||
.append("quantity", 1)
|
||||
.append("price", 800);
|
||||
UpdateResult updateResult = collection.updateOne(Filters.eq("customerId", 1023), Updates.addToSet("items", item));
|
||||
System.out.println("updateResult:- " + updateResult);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
||||
//
|
||||
// Connect to cluster (default is localhost:27017)
|
||||
//
|
||||
setUp();
|
||||
|
||||
//
|
||||
// Push document into the array using DBObject
|
||||
//
|
||||
|
||||
pushOperationUsingDBObject();
|
||||
|
||||
//
|
||||
// Push document into the array using Document.
|
||||
//
|
||||
|
||||
pushOperationUsingDocument();
|
||||
|
||||
//
|
||||
// Push document into the array using addToSet operator.
|
||||
//
|
||||
addToSetOperation();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.baeldung.mongo;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Updates;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
public class PushOperationLiveTest {
|
||||
|
||||
private static MongoClient mongoClient;
|
||||
private static MongoDatabase db;
|
||||
private static MongoCollection<Document> collection;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
if (mongoClient == null) {
|
||||
mongoClient = new MongoClient("localhost", 27017);
|
||||
db = mongoClient.getDatabase("baeldung");
|
||||
collection = db.getCollection("orders");
|
||||
|
||||
collection.insertOne(
|
||||
Document.parse("{\n" + " \"customerId\": 1023,\n" + " \"orderTimestamp\": NumberLong(\"1646460073000\"),\n" + " \"shippingDestination\": \"336, Street No.1 Pawai Mumbai\",\n" + " \"purchaseOrder\": 1000,\n"
|
||||
+ " \"contactNumber\":\"9898987676\",\n" + " \"items\": [ \n" + " {\n" + " \"itemName\": \"BERGER\",\n" + " \"quantity\": 1,\n" + " \"price\": 500\n" + " },\n"
|
||||
+ " {\n" + " \"itemName\": \"VEG PIZZA\",\n" + " \"quantity\": 1,\n" + " \"price\": 800\n" + " } \n" + " ]\n" + " }"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenOrderCollection_whenPushOperationUsingDBObject_thenCheckingForDocument() {
|
||||
|
||||
DBObject listItem = new BasicDBObject("items", new BasicDBObject("itemName", "PIZZA MANIA").append("quantity", 1)
|
||||
.append("price", 800));
|
||||
BasicDBObject searchFilter = new BasicDBObject("customerId", 1023);
|
||||
BasicDBObject updateQuery = new BasicDBObject();
|
||||
updateQuery.append("$push", listItem);
|
||||
UpdateResult updateResult = collection.updateOne(searchFilter, updateQuery);
|
||||
|
||||
Document orderDetail = collection.find(Filters.eq("customerId", 1023))
|
||||
.first();
|
||||
assertNotNull(orderDetail);
|
||||
assertFalse(orderDetail.isEmpty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenOrderCollection_whenPushOperationUsingDocument_thenCheckingForDocument() {
|
||||
|
||||
Document item = new Document().append("itemName", "PIZZA MANIA")
|
||||
.append("quantity", 1)
|
||||
.append("price", 800);
|
||||
UpdateResult updateResult = collection.updateOne(Filters.eq("customerId", 1023), Updates.push("items", item));
|
||||
|
||||
Document orderDetail = collection.find(Filters.eq("customerId", 1023))
|
||||
.first();
|
||||
assertNotNull(orderDetail);
|
||||
assertFalse(orderDetail.isEmpty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenOrderCollection_whenAddToSetOperation_thenCheckingForDocument() {
|
||||
|
||||
Document item = new Document().append("itemName", "PIZZA MANIA")
|
||||
.append("quantity", 1)
|
||||
.append("price", 800);
|
||||
UpdateResult updateResult = collection.updateOne(Filters.eq("customerId", 1023), Updates.addToSet("items", item));
|
||||
|
||||
Document orderDetail = collection.find(Filters.eq("customerId", 1023))
|
||||
.first();
|
||||
assertNotNull(orderDetail);
|
||||
assertFalse(orderDetail.isEmpty());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
mongoClient.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
<module>hbase</module>
|
||||
<module>hibernate5</module>
|
||||
<module>hibernate-mapping</module> <!-- long running -->
|
||||
<module>hibernate-mapping-2</module>
|
||||
<module>hibernate-ogm</module>
|
||||
<module>hibernate-annotations</module>
|
||||
<module>hibernate-exceptions</module>
|
||||
|
@ -73,6 +74,7 @@
|
|||
<module>spring-data-jpa-crud</module>
|
||||
<module>spring-data-jpa-crud-2</module>
|
||||
<module>spring-data-jpa-enterprise</module>
|
||||
<module>spring-data-jpa-enterprise-2</module>
|
||||
<module>spring-data-jpa-filtering</module>
|
||||
<module>spring-data-jpa-query</module>
|
||||
<module>spring-data-jpa-query-2</module>
|
||||
|
|
89
pom.xml
89
pom.xml
|
@ -7,9 +7,6 @@
|
|||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<modules>
|
||||
<module>spring-5-webflux-2</module>
|
||||
</modules>
|
||||
<name>parent-modules</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
@ -355,6 +352,7 @@
|
|||
<module>apache-libraries</module>
|
||||
<module>apache-olingo</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-poi-2</module>
|
||||
<module>apache-rocketmq</module>
|
||||
<module>apache-shiro</module>
|
||||
<module>apache-spark</module>
|
||||
|
@ -653,6 +651,7 @@
|
|||
<module>spring-cucumber</module>
|
||||
|
||||
<module>spring-data-rest</module>
|
||||
<module>spring-data-rest-2</module>
|
||||
<module>spring-data-rest-querydsl</module>
|
||||
<module>spring-di</module>
|
||||
<module>spring-di-2</module>
|
||||
|
@ -841,6 +840,7 @@
|
|||
<module>apache-libraries</module>
|
||||
<module>apache-olingo</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-poi-2</module>
|
||||
<module>apache-rocketmq</module>
|
||||
<module>apache-shiro</module>
|
||||
<module>apache-spark</module>
|
||||
|
@ -855,6 +855,7 @@
|
|||
<module>atomix</module>
|
||||
|
||||
<module>aws</module>
|
||||
<module>aws-app-sync</module>
|
||||
<module>aws-lambda</module>
|
||||
<module>aws-reactive</module>
|
||||
|
||||
|
@ -906,6 +907,7 @@
|
|||
<!-- <module>gradle-6</module> --> <!-- Not a maven project -->
|
||||
<!-- <module>grails</module> --> <!-- Not a maven project -->
|
||||
<module>graphql/graphql-java</module>
|
||||
<module>graphql/graphql-dgs</module>
|
||||
<module>grpc</module>
|
||||
<module>gson</module>
|
||||
<module>guava-modules</module>
|
||||
|
@ -967,6 +969,7 @@
|
|||
<module>ksqldb</module>
|
||||
|
||||
<!-- <module>lagom</module> --> <!-- Not a maven project -->
|
||||
<module>language-interop</module>
|
||||
<module>libraries-2</module>
|
||||
<module>libraries-3</module>
|
||||
|
||||
|
@ -981,6 +984,7 @@
|
|||
<module>libraries-http-2</module>
|
||||
<module>libraries-io</module>
|
||||
<module>libraries-primitive</module>
|
||||
<module>libraries-rpc</module>
|
||||
<module>libraries-security</module>
|
||||
<module>libraries-server</module>
|
||||
<module>libraries-server-2</module>
|
||||
|
@ -1096,6 +1100,7 @@
|
|||
<module>spring-5-reactive-oauth</module>
|
||||
<module>spring-5-reactive-security</module>
|
||||
<module>spring-5-webflux</module>
|
||||
<module>spring-5-webflux-2</module>
|
||||
<module>spring-reactive</module>
|
||||
|
||||
<module>spring-activiti</module>
|
||||
|
@ -1128,9 +1133,11 @@
|
|||
<module>spring-cucumber</module>
|
||||
|
||||
<module>spring-data-rest</module>
|
||||
<module>spring-data-rest-2</module>
|
||||
<module>spring-data-rest-querydsl</module>
|
||||
<module>spring-di</module>
|
||||
<module>spring-di-2</module>
|
||||
<module>spring-di-3</module>
|
||||
<module>spring-drools</module>
|
||||
|
||||
<module>spring-ejb</module>
|
||||
|
@ -1168,6 +1175,8 @@
|
|||
<module>spring-static-resources</module>
|
||||
<module>spring-swagger-codegen</module>
|
||||
|
||||
<module>spring-threads</module>
|
||||
|
||||
<module>spring-vault</module>
|
||||
<module>spring-vertx</module>
|
||||
|
||||
|
@ -1317,39 +1326,43 @@
|
|||
<module>core-java-modules/core-java-9-improvements</module>
|
||||
<module>core-java-modules/core-java-9-jigsaw</module>
|
||||
<!-- <module>core-java-modules/core-java-9-new-features</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<module>core-java-modules/core-java-9-streams</module>
|
||||
<module>core-java-modules/core-java-10</module>
|
||||
<module>core-java-modules/core-java-11</module>
|
||||
<module>core-java-modules/core-java-11-2</module>
|
||||
<!-- <module>core-java-modules/core-java-12</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-13</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-14</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-15</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<module>core-java-modules/core-java-collections-set</module>
|
||||
<module>core-java-modules/core-java-collections-maps-4</module>
|
||||
<module>core-java-modules/core-java-date-operations-1</module>
|
||||
<module>core-java-modules/core-java-datetime-conversion</module>
|
||||
<module>core-java-modules/core-java-datetime-string</module>
|
||||
<module>core-java-modules/core-java-io-conversions-2</module>
|
||||
<module>core-java-modules/core-java-jpms</module>
|
||||
<module>core-java-modules/core-java-os</module>
|
||||
<module>core-java-modules/core-java-string-algorithms-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-4</module>
|
||||
<module>core-java-modules/core-java-time-measurements</module>
|
||||
<module>core-java-modules/core-java-networking-3</module>
|
||||
<module>core-java-modules/multimodulemavenproject</module>
|
||||
<module>ddd-modules</module>
|
||||
<module>httpclient-2</module>
|
||||
<module>libraries-concurrency</module>
|
||||
<module>persistence-modules/sirix</module>
|
||||
<module>persistence-modules/spring-data-cassandra-2</module>
|
||||
<module>quarkus-vs-springboot</module>
|
||||
<module>quarkus-jandex</module>
|
||||
<module>spring-boot-modules/spring-boot-cassandre</module>
|
||||
<module>spring-boot-modules/spring-boot-camel</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
<module>core-java-modules/core-java-9-streams</module>
|
||||
<module>core-java-modules/core-java-10</module>
|
||||
<module>core-java-modules/core-java-11</module>
|
||||
<module>core-java-modules/core-java-11-2</module>
|
||||
<!-- <module>core-java-modules/core-java-12</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-13</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-14</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-15</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-16</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-17</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<module>core-java-modules/core-java-collections-set</module>
|
||||
<module>core-java-modules/core-java-collections-maps-4</module>
|
||||
<module>core-java-modules/core-java-date-operations-1</module>
|
||||
<module>core-java-modules/core-java-datetime-conversion</module>
|
||||
<module>core-java-modules/core-java-datetime-string</module>
|
||||
<module>core-java-modules/core-java-io-conversions-2</module>
|
||||
<module>core-java-modules/core-java-jpms</module>
|
||||
<module>core-java-modules/core-java-os</module>
|
||||
<module>core-java-modules/core-java-string-algorithms-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-4</module>
|
||||
<module>core-java-modules/core-java-time-measurements</module>
|
||||
<module>core-java-modules/core-java-networking-3</module>
|
||||
<module>core-java-modules/multimodulemavenproject</module>
|
||||
<module>core-java-modules/core-java-strings</module>
|
||||
<module>ddd-modules</module>
|
||||
<module>docker</module>
|
||||
<module>httpclient-2</module>
|
||||
<module>libraries-concurrency</module>
|
||||
<module>persistence-modules/sirix</module>
|
||||
<module>persistence-modules/spring-data-cassandra-2</module>
|
||||
<module>quarkus-vs-springboot</module>
|
||||
<module>quarkus-jandex</module>
|
||||
<module>spring-boot-modules/spring-boot-cassandre</module>
|
||||
<module>spring-boot-modules/spring-boot-camel</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
|
@ -1388,6 +1401,8 @@
|
|||
<!-- <module>core-java-modules/core-java-13</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-14</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-15</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-16</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<!-- <module>core-java-modules/core-java-17</module> --> <!-- uses preview features, to be decided how to handle -->
|
||||
<module>core-java-modules/core-java-collections-set</module>
|
||||
<module>core-java-modules/core-java-collections-maps-4</module>
|
||||
<module>core-java-modules/core-java-date-operations-1</module>
|
||||
|
@ -1396,6 +1411,7 @@
|
|||
<module>core-java-modules/core-java-io-conversions-2</module>
|
||||
<module>core-java-modules/core-java-jpms</module>
|
||||
<module>core-java-modules/core-java-os</module>
|
||||
<module>core-java-modules/core-java-string-algorithms-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-3</module>
|
||||
<module>core-java-modules/core-java-string-operations-4</module>
|
||||
<module>core-java-modules/core-java-time-measurements</module>
|
||||
|
@ -1403,6 +1419,7 @@
|
|||
<module>core-java-modules/multimodulemavenproject</module>
|
||||
<module>core-java-modules/core-java-strings</module>
|
||||
<module>ddd-modules</module>
|
||||
<module>docker</module>
|
||||
<module>httpclient-2</module>
|
||||
<module>libraries-concurrency</module>
|
||||
<module>persistence-modules/sirix</module>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.keycloak;
|
||||
|
||||
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class KeycloakConfig {
|
||||
|
||||
@Bean
|
||||
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
|
||||
return new KeycloakSpringBootConfigResolver();
|
||||
}
|
||||
}
|
|
@ -23,11 +23,6 @@ class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
|||
auth.authenticationProvider(keycloakAuthenticationProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
|
||||
return new KeycloakSpringBootConfigResolver();
|
||||
}
|
||||
|
||||
// Specifies the session authentication strategy
|
||||
@Bean
|
||||
@Override
|
||||
|
|
|
@ -45,9 +45,11 @@
|
|||
<module>spring-cloud-ribbon-retry</module>
|
||||
<module>spring-cloud-circuit-breaker</module>
|
||||
<module>spring-cloud-eureka-self-preservation</module>
|
||||
<!--<module>spring-cloud-openfeign</module> --> <!-- Fixing in JAVA-2820 -->
|
||||
<!-- <module>spring-cloud-openfeign</module> --> <!-- Fixing under JAVA-10446 -->
|
||||
<!-- <module>spring-cloud-netflix-feign</module> --> <!-- Fixing under JAVA-10431 -->
|
||||
<module>spring-cloud-sentinel</module>
|
||||
<module>spring-cloud-dapr</module>
|
||||
<module>spring-cloud-docker</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<module>wildfly-jpa</module>
|
||||
<module>wildfly-ejb-interfaces</module>
|
||||
<module>wildfly-ejb</module>
|
||||
<module>wildfly-mdb</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-security-web-boot-3</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
@ -23,6 +24,15 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.mongoauth;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.baeldung.mongoauth.config.MongoConfig;
|
||||
import com.baeldung.mongoauth.config.SecurityConfig;
|
||||
|
||||
@SpringBootApplication
|
||||
@Import({ SecurityConfig.class, MongoConfig.class })
|
||||
public class MongoAuthApplication {
|
||||
|
||||
public static void main(String... args) {
|
||||
SpringApplication.run(MongoAuthApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.baeldung.mongoauth.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import com.mongodb.client.MongoClients;
|
||||
|
||||
import de.flapdoodle.embed.mongo.MongodExecutable;
|
||||
import de.flapdoodle.embed.mongo.MongodStarter;
|
||||
import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig;
|
||||
import de.flapdoodle.embed.mongo.config.MongodConfig;
|
||||
import de.flapdoodle.embed.mongo.config.Net;
|
||||
import de.flapdoodle.embed.mongo.distribution.Version;
|
||||
import de.flapdoodle.embed.process.runtime.Network;
|
||||
|
||||
@Configuration
|
||||
public class MongoConfig {
|
||||
|
||||
private static final String CONNECTION_STRING = "mongodb://%s:%d";
|
||||
private static final String HOST = "localhost";
|
||||
|
||||
@Bean
|
||||
public MongoTemplate mongoTemplate() throws Exception {
|
||||
|
||||
int randomPort = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
ImmutableMongodConfig mongoDbConfig = MongodConfig.builder()
|
||||
.version(Version.Main.PRODUCTION)
|
||||
.net(new Net(HOST, randomPort, Network.localhostIsIPv6()))
|
||||
.build();
|
||||
|
||||
MongodStarter starter = MongodStarter.getDefaultInstance();
|
||||
MongodExecutable mongodExecutable = starter.prepare(mongoDbConfig);
|
||||
mongodExecutable.start();
|
||||
return new MongoTemplate(MongoClients.create(String.format(CONNECTION_STRING, HOST, randomPort)), "mongo_auth");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.baeldung.mongoauth.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
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.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
public SecurityConfig(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager customAuthenticationManager() throws Exception {
|
||||
return authenticationManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(@Autowired AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(bCryptPasswordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf()
|
||||
.disable()
|
||||
.authorizeRequests()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.anyRequest()
|
||||
.permitAll()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.mongoauth.controller;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ResourceController {
|
||||
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@GetMapping("/admin")
|
||||
public String admin() {
|
||||
return "Hello Admin!";
|
||||
}
|
||||
|
||||
@RolesAllowed({ "ROLE_ADMIN", "ROLE_USER" })
|
||||
@GetMapping("/user")
|
||||
public String user() {
|
||||
return "Hello User!";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.mongoauth.domain;
|
||||
|
||||
public class Role {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package com.baeldung.mongoauth.domain;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoId;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
@Document
|
||||
public class User implements UserDetails {
|
||||
private @MongoId ObjectId id;
|
||||
private String username;
|
||||
private String password;
|
||||
private Set<UserRole> userRoles;
|
||||
|
||||
public ObjectId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setUserRoles(Set<UserRole> userRoles) {
|
||||
this.userRoles = userRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserRole> getAuthorities() {
|
||||
return this.userRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
User user = (User) o;
|
||||
return Objects.equals(username, user.username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.mongoauth.domain;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public class UserRole implements GrantedAuthority {
|
||||
|
||||
private Role role;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return role.getName();
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(Role role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.mongoauth.repository;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
|
||||
import com.baeldung.mongoauth.domain.User;
|
||||
|
||||
public interface UserRepository extends MongoRepository<User, String> {
|
||||
|
||||
@Query("{username:'?0'}")
|
||||
User findUserByUsername(String username);
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.mongoauth.service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
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 com.baeldung.mongoauth.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
public class MongoAuthUserDetailService implements UserDetailsService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public MongoAuthUserDetailService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
|
||||
|
||||
com.baeldung.mongoauth.domain.User user = userRepository.findUserByUsername(userName);
|
||||
|
||||
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
|
||||
|
||||
user.getAuthorities()
|
||||
.forEach(role -> {
|
||||
grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole()
|
||||
.getName()));
|
||||
});
|
||||
|
||||
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.baeldung.mongoauth.service;
|
||||
|
||||
public interface SecurityService {
|
||||
boolean login(String username, String password);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.baeldung.mongoauth.service;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SecurityServiceImpl implements SecurityService {
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
public SecurityServiceImpl(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login(String username, String password) {
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
|
||||
|
||||
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(usernamePasswordAuthenticationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
spring.mongodb.embedded.version=4.4.9
|
|
@ -0,0 +1,118 @@
|
|||
package com.baeldung.mongoauth;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
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.status;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.baeldung.mongoauth.domain.Role;
|
||||
import com.baeldung.mongoauth.domain.User;
|
||||
import com.baeldung.mongoauth.domain.UserRole;
|
||||
|
||||
@SpringBootTest(classes = { MongoAuthApplication.class })
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
class MongoAuthApplicationIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
@Autowired
|
||||
private BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
private static final String USER_NAME = "user@gmail.com";
|
||||
private static final String ADMIN_NAME = "admin@gmail.com";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
|
||||
setUp();
|
||||
|
||||
mvc = MockMvcBuilders.webAppContextSetup(context)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void setUp() {
|
||||
Role roleUser = new Role();
|
||||
roleUser.setName("ROLE_USER");
|
||||
mongoTemplate.save(roleUser);
|
||||
|
||||
User user = new User();
|
||||
user.setUsername(USER_NAME);
|
||||
user.setPassword(bCryptPasswordEncoder.encode(PASSWORD));
|
||||
|
||||
UserRole userRole = new UserRole();
|
||||
userRole.setRole(roleUser);
|
||||
user.setUserRoles(new HashSet<>(Collections.singletonList(userRole)));
|
||||
mongoTemplate.save(user);
|
||||
|
||||
User admin = new User();
|
||||
admin.setUsername(ADMIN_NAME);
|
||||
admin.setPassword(bCryptPasswordEncoder.encode(PASSWORD));
|
||||
|
||||
Role roleAdmin = new Role();
|
||||
roleAdmin.setName("ROLE_ADMIN");
|
||||
mongoTemplate.save(roleAdmin);
|
||||
|
||||
UserRole adminRole = new UserRole();
|
||||
adminRole.setRole(roleAdmin);
|
||||
admin.setUserRoles(new HashSet<>(Collections.singletonList(adminRole)));
|
||||
mongoTemplate.save(admin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUserCredentials_whenInvokeUserAuthorizedEndPoint_thenReturn200() throws Exception {
|
||||
mvc.perform(get("/user").with(httpBasic(USER_NAME, PASSWORD)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUserNotExists_whenInvokeEndPoint_thenReturn401() throws Exception {
|
||||
mvc.perform(get("/user").with(httpBasic("not_existing_user", "password")))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUserExistsAndWrongPassword_whenInvokeEndPoint_thenReturn401() throws Exception {
|
||||
mvc.perform(get("/user").with(httpBasic(USER_NAME, "wrong_password")))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUserCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn403() throws Exception {
|
||||
mvc.perform(get("/admin").with(httpBasic(USER_NAME, PASSWORD)))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAdminCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn200() throws Exception {
|
||||
mvc.perform(get("/admin").with(httpBasic(ADMIN_NAME, PASSWORD)))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
mvc.perform(get("/user").with(httpBasic(ADMIN_NAME, PASSWORD)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue