BAEL-6254: Scoped values (#13756)

This commit is contained in:
Daniel Strmecki 2023-05-03 22:00:47 +02:00 committed by GitHub
parent f954094e2b
commit 078826eef5
20 changed files with 584 additions and 0 deletions

View File

@ -0,0 +1,2 @@
## Relevant Articles
- TBD

View File

@ -0,0 +1,65 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.baeldung.core-java-modules</groupId>
<artifactId>core-java-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>core-java-20</artifactId>
<properties>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>20</source>
<target>20</target>
<compilerArgs>
<arg>--enable-preview</arg>
<arg>--add-modules=jdk.incubator.concurrent</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview --add-modules=jdk.incubator.concurrent</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,33 @@
package com.baeldung.scopedvalues.classic;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
public class Controller {
private final Service service = new Service();
public void processRequest(HttpServletRequest request, HttpServletResponse response, User loggedInUser) {
Optional<Data> data = service.getData(request, loggedInUser);
if (data.isPresent()) {
try {
PrintWriter out = response.getWriter();
response.setContentType("application/json");
out.print(data.get());
out.flush();
response.setStatus(200);
} catch (IOException e) {
response.setStatus(500);
}
} else {
response.setStatus(400);
}
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.scopedvalues.classic;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import java.util.Optional;
public class Repository {
public Optional<Data> getData(String id, User user) {
return user.isAdmin()
? Optional.of(new Data(id, "Title 1", "Description 1"))
: Optional.empty();
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.scopedvalues.classic;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
public class Server {
private static final List<User> AUTHENTICATED_USERS = List.of(
new User("1", "admin", "123456", true),
new User("2", "user", "123456", false)
);
private final Controller controller = new Controller();
private final ExecutorService executor = Executors.newFixedThreadPool(5);
public void serve(HttpServletRequest request, HttpServletResponse response) throws InterruptedException, ExecutionException {
Optional<User> user = authenticateUser(request);
if (user.isPresent()) {
Future<?> future = executor.submit(() ->
controller.processRequest(request, response, user.get()));
future.get();
} else {
response.setStatus(401);
}
}
private Optional<User> authenticateUser(HttpServletRequest request) {
return AUTHENTICATED_USERS.stream()
.filter(user -> checkUserPassword(user, request))
.findFirst();
}
private boolean checkUserPassword(User user, HttpServletRequest request) {
return user.name().equals(request.getParameter("user_name"))
&& user.password().equals(request.getParameter("user_pw"));
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.scopedvalues.classic;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
public class Service {
private final Repository repository = new Repository();
public Optional<Data> getData(HttpServletRequest request, User loggedInUser) {
String id = request.getParameter("data_id");
return repository.getData(id, loggedInUser);
}
}

View File

@ -0,0 +1,3 @@
package com.baeldung.scopedvalues.data;
public record Data(String id, String title, String description) {}

View File

@ -0,0 +1,3 @@
package com.baeldung.scopedvalues.data;
public record User(String id, String name, String password, boolean isAdmin) {}

View File

@ -0,0 +1,37 @@
package com.baeldung.scopedvalues.scoped;
import com.baeldung.scopedvalues.data.Data;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jdk.incubator.concurrent.ScopedValue;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
public class Controller {
private final Service internalService = new Service();
public void processRequest(HttpServletRequest request, HttpServletResponse response) {
Optional<Data> data = internalService.getData(request);
ScopedValue.where(Server.LOGGED_IN_USER, null)
.run(internalService::extractData);
if (data.isPresent()) {
try {
PrintWriter out = response.getWriter();
response.setContentType("application/json");
out.print(data.get());
out.flush();
response.setStatus(200);
} catch (IOException e) {
response.setStatus(500);
}
} else {
response.setStatus(400);
}
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.scopedvalues.scoped;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import java.util.Optional;
public class Repository {
public Optional<Data> getData(String id) {
User loggedInUser = Server.LOGGED_IN_USER.get();
return loggedInUser.isAdmin()
? Optional.of(new Data(id, "Title 1", "Description 1"))
: Optional.empty();
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.scopedvalues.scoped;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jdk.incubator.concurrent.ScopedValue;
import java.util.List;
import java.util.Optional;
public class Server {
private static final List<User> AUTHENTICATED_USERS = List.of(
new User("1", "admin", "123456", true),
new User("2", "user", "123456", false)
);
public final static ScopedValue<User> LOGGED_IN_USER = ScopedValue.newInstance();
private final Controller controller = new Controller();
public void serve(HttpServletRequest request, HttpServletResponse response) {
Optional<User> user = authenticateUser(request);
if (user.isPresent()) {
ScopedValue.where(LOGGED_IN_USER, user.get())
.run(() -> controller.processRequest(request, response));
} else {
response.setStatus(401);
}
}
private Optional<User> authenticateUser(HttpServletRequest request) {
return AUTHENTICATED_USERS.stream()
.filter(user -> checkUserPassword(user, request))
.findFirst();
}
private boolean checkUserPassword(User user, HttpServletRequest request) {
return user.name().equals(request.getParameter("user_name"))
&& user.password().equals(request.getParameter("user_pw"));
}
}

View File

@ -0,0 +1,23 @@
package com.baeldung.scopedvalues.scoped;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
public class Service {
private final Repository repository = new Repository();
public Optional<Data> getData(HttpServletRequest request) {
String id = request.getParameter("data_id");
return repository.getData(id);
}
public void extractData() {
User loggedInUser = Server.LOGGED_IN_USER.get();
assert loggedInUser == null;
}
}

View File

@ -0,0 +1,44 @@
package com.baeldung.scopedvalues.scoped.inheriting;
import com.baeldung.scopedvalues.data.Data;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jdk.incubator.concurrent.StructuredTaskScope;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Controller {
private final InternalService internalService = new InternalService();
private final ExternalService externalService = new ExternalService();
public void processRequest(HttpServletRequest request, HttpServletResponse response) {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<Optional<Data>> internalData = scope.fork(() -> internalService.getData(request));
Future<String> externalData = scope.fork(externalService::getData);
try {
scope.join();
scope.throwIfFailed();
Optional<Data> data = internalData.resultNow();
if (data.isPresent()) {
PrintWriter out = response.getWriter();
response.setContentType("application/json");
out.println(data.get());
out.print(externalData.resultNow());
out.flush();
response.setStatus(200);
} else {
response.setStatus(400);
}
} catch (InterruptedException | ExecutionException | IOException e) {
response.setStatus(500);
}
}
}
}

View File

@ -0,0 +1,9 @@
package com.baeldung.scopedvalues.scoped.inheriting;
public class ExternalService {
public String getData() {
return "External data";
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.scopedvalues.scoped.inheriting;
import com.baeldung.scopedvalues.data.Data;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
public class InternalService {
private final Repository repository = new Repository();
public Optional<Data> getData(HttpServletRequest request) {
String id = request.getParameter("data_id");
return repository.getData(id);
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.scopedvalues.scoped.inheriting;
import com.baeldung.scopedvalues.data.Data;
import com.baeldung.scopedvalues.data.User;
import java.util.Optional;
public class Repository {
public Optional<Data> getData(String id) {
User loggedInUser = Server.LOGGED_IN_USER.get();
return loggedInUser.isAdmin()
? Optional.of(new Data(id, "Title 1", "Description 1"))
: Optional.empty();
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.scopedvalues.scoped.inheriting;
import com.baeldung.scopedvalues.data.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jdk.incubator.concurrent.ScopedValue;
import java.util.List;
import java.util.Optional;
public class Server {
private static final List<User> AUTHENTICATED_USERS = List.of(
new User("1", "admin", "123456", true),
new User("2", "user", "123456", false)
);
public final static ScopedValue<User> LOGGED_IN_USER = ScopedValue.newInstance();
private final Controller controller = new Controller();
public void serve(HttpServletRequest request, HttpServletResponse response) {
Optional<User> user = authenticateUser(request);
if (user.isPresent()) {
ScopedValue.where(LOGGED_IN_USER, user.get())
.run(() -> controller.processRequest(request, response));
} else {
response.setStatus(401);
}
}
private Optional<User> authenticateUser(HttpServletRequest request) {
return AUTHENTICATED_USERS.stream()
.filter(user -> checkUserPassword(user, request))
.findFirst();
}
private boolean checkUserPassword(User user, HttpServletRequest request) {
return user.name().equals(request.getParameter("user_name"))
&& user.password().equals(request.getParameter("user_pw"));
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.scopedvalues.classic;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutionException;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
public class ServerUnitTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private final StringWriter writer = new StringWriter();
private final Server server = new Server();
@Test
void givenMockedRequestWithAdminCredentials_whenServeMethodIsCalled_thenDataIsReturned() throws InterruptedException, IOException, ExecutionException {
when(request.getParameter("user_name")).thenReturn("admin");
when(request.getParameter("user_pw")).thenReturn("123456");
when(request.getParameter("data_id")).thenReturn("1");
when(response.getWriter()).thenReturn(new PrintWriter(writer));
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("Data[id=1, title=Title 1, description=Description 1]");
}
@Test
void givenMockedRequestWithUserCredentials_whenServeMethodIsCalled_thenNoDataIsReturned() throws InterruptedException, ExecutionException {
when(request.getParameter("user_name")).thenReturn("user");
when(request.getParameter("user_pw")).thenReturn("123456");
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("");
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.scopedvalues.scoped;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class ServerUnitTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private final StringWriter writer = new StringWriter();
private final Server server = new Server();
@Test
void givenMockedRequestWithAdminCredentials_whenServeMethodIsCalled_thenDataIsReturned() throws IOException {
when(request.getParameter("user_name")).thenReturn("admin");
when(request.getParameter("user_pw")).thenReturn("123456");
when(request.getParameter("data_id")).thenReturn("1");
when(response.getWriter()).thenReturn(new PrintWriter(writer));
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("Data[id=1, title=Title 1, description=Description 1]");
}
@Test
void givenMockedRequestWithUserCredentials_whenServeMethodIsCalled_thenNoDataIsReturned() throws IOException {
when(request.getParameter("user_name")).thenReturn("user");
when(request.getParameter("user_pw")).thenReturn("123456");
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("");
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.scopedvalues.scoped.inheriting;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class ServerUnitTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private final StringWriter writer = new StringWriter();
private final Server server = new Server();
@Test
void givenMockedRequestWithAdminCredentials_whenServeMethodIsCalled_thenDataIsReturned() throws IOException {
when(request.getParameter("user_name")).thenReturn("admin");
when(request.getParameter("user_pw")).thenReturn("123456");
when(request.getParameter("data_id")).thenReturn("1");
when(response.getWriter()).thenReturn(new PrintWriter(writer));
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("Data[id=1, title=Title 1, description=Description 1]\nExternal data");
}
@Test
void givenMockedRequestWithUserCredentials_whenServeMethodIsCalled_thenNoDataIsReturned() throws IOException {
when(request.getParameter("user_name")).thenReturn("user");
when(request.getParameter("user_pw")).thenReturn("123456");
server.serve(request, response);
assertThat(writer.toString()).isEqualTo("");
}
}