Skip to content

Commit 28a407f

Browse files
committed
Add lab content.
1 parent 8b2674d commit 28a407f

File tree

128 files changed

+5640
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+5640
-2
lines changed

.idea/.gitignore

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.vscode/settings.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"java.project.sourcePaths": [
3+
"A_bday_java/src/main/java",
4+
"B_bday_javadoc/src/main/java",
5+
"C_bday_jwebserver/src/main/java",
6+
"D_bday_jlink/src/main/java",
7+
"E_bday_jfr/src/main/java",
8+
],
9+
"java.project.referencedLibraries": [
10+
"lib/**/*.jar"
11+
],
12+
"java.project.outputPath": "out"
13+
}

A_bday_java/A_bday_java.iml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="JAVA_MODULE" version="4">
3+
<component name="NewModuleRootManager" inherit-compiler-output="true">
4+
<exclude-output />
5+
<content url="file://$MODULE_DIR$">
6+
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
7+
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
8+
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
9+
</content>
10+
<orderEntry type="inheritedJdk" />
11+
<orderEntry type="sourceFolder" forTests="false" />
12+
<orderEntry type="library" name="lib" level="project" />
13+
<orderEntry type="module-library" scope="TEST">
14+
<library name="JUnit5.8.1">
15+
<CLASSES>
16+
<root url="jar://$MODULE_DIR$/../lib/junit-jupiter-5.8.1.jar!/" />
17+
<root url="jar://$MODULE_DIR$/../lib/junit-jupiter-api-5.8.1.jar!/" />
18+
<root url="jar://$MODULE_DIR$/../lib/opentest4j-1.2.0.jar!/" />
19+
<root url="jar://$MODULE_DIR$/../lib/junit-platform-commons-1.8.1.jar!/" />
20+
<root url="jar://$MODULE_DIR$/../lib/apiguardian-api-1.1.2.jar!/" />
21+
<root url="jar://$MODULE_DIR$/../lib/junit-jupiter-params-5.8.1.jar!/" />
22+
<root url="jar://$MODULE_DIR$/../lib/junit-jupiter-engine-5.8.1.jar!/" />
23+
<root url="jar://$MODULE_DIR$/../lib/junit-platform-engine-1.8.1.jar!/" />
24+
</CLASSES>
25+
<JAVADOC />
26+
<SOURCES />
27+
</library>
28+
</orderEntry>
29+
</component>
30+
</module>

A_bday_java/README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Working with the Java Launcher
2+
3+
## Project Structure
4+
5+
Here's a breakdown of the different project components:
6+
7+
* `src/main/java`: The source code for the project, organized by package:
8+
* `eu.ammbra.bday.Organizer.java`: The main entry point of the application, possibly responsible for coordinating the overall flow of the program.
9+
* `eu.ammbra.bday.details`: A package containing domain model classes. These classes represent entities involved in birthday celebrations, such as cakes, parties, and people attending them.
10+
* `eu.ammbra.bday.handlers`: A package containing classes responsible for handling specific tasks.
11+
* `eu.ammbra.bday.operations`: A package containing classes responsible for handling the operational side of parties.
12+
* `eu.ammbra.bday.store`: A package containing classes responsible for interacting with data from files.
13+
* `src/main/resources/store/events.json`: A resource file containing sample event data in JSON format.
14+
* `src/test/java`: The source code for unit tests and integration tests.
15+
16+
## **Lab Activity No 1**: Launch Multiple Java Files
17+
18+
Since JDK 22 you can use the Java launcher to [execute source-code programs provided as multiple files](https://docs.oracle.com/en/java/javase/23/docs/specs/man/java.html#using-source-file-mode-to-launch-source-code-programs).
19+
20+
```shell
21+
java --class-path '*' Prog1.java
22+
```
23+
This activity relies on you to execute the following steps:
24+
25+
1. Open a terminal window in the `lab-jdk-tools/A_bday_java` folder. You can use the terminal of your IDE/code editor.
26+
2. Check that $JAVA_HOME is properly set by trying `echo $JAVA_HOME`.
27+
3. Inside the terminal, execute the `java` command to launch `Organizer` class from `src/main/java/eu/ammbra/bday/`.
28+
Pay attention to the following:
29+
30+
* classpath libraries located in `lab-jdk-tools/lib` folder.
31+
* the project uses preview features so do not forget to enable those via `--enable-preview`
32+
33+
## Solution
34+
35+
&rarr; [Click to see the solution](SOLUTION.md#lab-activity-no-1-launch-multiple-java-files)
36+
37+
## **Lab Activity No 2**: Use the Java Command-Line Argument Files
38+
39+
When working with the `java` launcher, you can shorten or simplify the `java` command by using `@` argument files to specify one or more text files that contain arguments.
40+
The arguments can be options and class names, which are then passed to the `java` command. [This](https://docs.oracle.com/en/java/javase/23/docs/specs/man/java.html#java-command-line-argument-files) allows you to :
41+
42+
* create `java` commands of any length on any operating system
43+
* for containerized applications, easily swap the content the argument files without modifying the Dockerfile.
44+
45+
This activity requires you to:
46+
47+
1. Create a file and copy the arguments you used to launch the `Organizer` class from `src/main/java/eu/ammbra/bday/`.
48+
2. Now launch again the `Organizer` class by providing the file as argument `java @your-arg-file-name`.
49+
50+
## Solution
51+
52+
&rarr; [Click to see the solution](SOLUTION.md#lab-activity-no-2-use-the-java-command-line-argument-files)
53+
54+
Next step is to [create some documentation](../B_bday_javadoc/README.md)!
55+
56+

A_bday_java/SOLUTION.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Solutions for Working with the Java Launcher
2+
3+
## **Lab Activity No 1**: Launch Multiple Java Files
4+
5+
<details>
6+
<summary>Click to expand</summary>
7+
8+
```shell
9+
10+
# Unix and macOS compatible command
11+
12+
java --enable-preview -cp "../lib/*" src/main/java/eu/ammbra/bday/Organizer.java
13+
14+
# Windows compatible command
15+
16+
java --enable-preview -cp "..\lib\*" src\main\java\eu\ammbra\bday\Organizer.java
17+
18+
```
19+
20+
</details>
21+
22+
## **Lab Activity No 2**: Use the Java Command-Line Argument Files
23+
24+
<details>
25+
<summary>Click to expand</summary>
26+
27+
```shell
28+
# make sure you copied `--enable-preview -cp "../lib/*" src/main/java/eu/ammbra/bday/Organizer.java`
29+
java @my-arg-file
30+
31+
```
32+
33+
</details>
34+
35+
[Let's go and create some documentation](../B_bday_javadoc/README.md)!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package eu.ammbra.bday;
2+
3+
import com.sun.net.httpserver.HttpServer;
4+
import eu.ammbra.bday.handlers.PartyHandler;
5+
import eu.ammbra.bday.store.DataStore;
6+
import eu.ammbra.bday.operations.PartyService;
7+
8+
import java.io.IOException;
9+
import java.net.InetSocketAddress;
10+
11+
12+
/// Main server application for an application responsible for organizing a birthday party.
13+
/// This server serves both static files and dynamic API endpoints.
14+
///
15+
public class Organizer {
16+
private static final int PORT = 8081;
17+
18+
19+
/// Starts the HTTP server to serve static files and dynamic store.
20+
///
21+
/// @param args Command-line arguments that may contain the path to static JSON file.
22+
/// @throws IOException If the server cannot start.
23+
///
24+
public static void main(String[] args) throws IOException {
25+
String filename = (args.length > 0) ? args[0] : "./src/main/resources/store/events.json";
26+
DataStore store = new DataStore(filename);
27+
PartyService service = new PartyService(store);
28+
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
29+
server.createContext("/api/organize", new PartyHandler(service));
30+
System.out.printf("Birthday Party Server is running at http://127.0.0.1:%d/api/organize%n", PORT);
31+
server.start();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package eu.ammbra.bday.details;
2+
3+
public record Cake(String flavor, int layers, double weight) {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package eu.ammbra.bday.details;
2+
3+
public record Celebration(long id, Person person, String flavor, String location, int attendees) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package eu.ammbra.bday.details;
2+
3+
public record Party(long id, String name, Cake cake, String location, int attendees) {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package eu.ammbra.bday.details;
2+
3+
import java.time.LocalDate;
4+
5+
public record Person(String firstName, String lastName, LocalDate date) {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package eu.ammbra.bday.handlers;
2+
3+
import com.google.gson.*;
4+
5+
import java.lang.reflect.Type;
6+
import java.time.LocalDate;
7+
import java.time.format.DateTimeFormatter;
8+
9+
/// Custom adapter for serializing and deserializing {@link LocalDate} objects to/from JSON strings.
10+
///
11+
/// This adapter uses the ISO date format `yyyy-MM-dd` for serialization and deserialization.
12+
///
13+
public class LocalDateTypeAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
14+
15+
/// Formatter used for parsing and formatting dates.
16+
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
17+
18+
/// Serializes a {@link LocalDate} object to a JSON string.
19+
///
20+
/// @param date the date to serialize
21+
/// @param srcType the type of the source object
22+
/// @param context the serialization context
23+
/// @return a JSON primitive representing the serialized date
24+
///
25+
@Override
26+
public JsonElement serialize(final LocalDate date, final Type srcType,
27+
final JsonSerializationContext context) {
28+
return new JsonPrimitive(date.format(formatter));
29+
}
30+
31+
/// Deserializes a JSON string to a {@link LocalDate} object.
32+
///
33+
/// @param json the JSON element to deserialize
34+
/// @param targetType the type of the target object
35+
/// @param context the deserialization context
36+
/// @return the deserialized date
37+
/// @throws JsonParseException if the JSON element cannot be parsed
38+
///
39+
@Override
40+
public LocalDate deserialize(final JsonElement json, final Type targetType,
41+
final JsonDeserializationContext context) throws JsonParseException {
42+
return LocalDate.parse(json.getAsString(), formatter);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package eu.ammbra.bday.handlers;
2+
3+
import com.google.gson.Gson;
4+
import com.sun.net.httpserver.HttpHandler;
5+
import com.sun.net.httpserver.HttpExchange;
6+
import eu.ammbra.bday.details.Party;
7+
import eu.ammbra.bday.operations.PartyNotFoundException;
8+
import eu.ammbra.bday.operations.PartyService;
9+
10+
import java.io.IOException;
11+
import java.io.OutputStream;
12+
import java.nio.charset.StandardCharsets;
13+
import java.util.List;
14+
15+
16+
///Handles HTTP requests for party-related store.
17+
///
18+
public class PartyHandler implements HttpHandler {
19+
20+
private final PartyService service;
21+
22+
23+
///Constructs a new PartyHandler instance with the specified service.
24+
///
25+
///@param service the party service instance
26+
///
27+
public PartyHandler(PartyService service) {
28+
this.service = service;
29+
}
30+
31+
32+
///Handles an HTTP request for party-related store.
33+
///
34+
/// @param exchange the HTTP exchange object
35+
/// @throws IOException if an I/O error occurs
36+
///
37+
@Override
38+
public void handle(HttpExchange exchange) throws IOException {
39+
if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) {
40+
StringBuilder result = new StringBuilder();
41+
int statusCode = 200;
42+
43+
String path = exchange.getRequestURI().getPath();
44+
45+
String[] segments = path.split("/");
46+
47+
switch (segments.length) {
48+
case int _ when (segments.length < 3 || !segments[2].equals("organize")) -> {
49+
result.append("Invalid endpoint");
50+
statusCode = 404;
51+
}
52+
case 3 -> {
53+
List<Party> parties = service.processAllParties();
54+
result.append(new Gson().toJson(parties));
55+
}
56+
case int _ -> {
57+
try {
58+
Party party = service.processPartyById(Long.parseLong(segments[3]));
59+
result.append(new Gson().toJson(party));
60+
} catch (PartyNotFoundException e) {
61+
result.append(e.getMessage());
62+
}
63+
}
64+
}
65+
66+
exchange.getResponseHeaders().set("Content-Type", "application/json");
67+
String response = result.toString();
68+
exchange.sendResponseHeaders(statusCode, response.getBytes().length);
69+
70+
try (OutputStream os = exchange.getResponseBody()) {
71+
os.write(response.getBytes(StandardCharsets.UTF_8));
72+
}
73+
} else {
74+
exchange.sendResponseHeaders(405, -1);
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)