Skip to content

Commit 4b1824b

Browse files
committed
Refactor to a standalone Java weather application using JDK HTTP server; update configurations, remove Spring dependencies, and enhance frontend design.
1 parent 2bb59ab commit 4b1824b

19 files changed

Lines changed: 608 additions & 199 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"vscode": {
1616
"extensions": [
1717
"vscjava.vscode-java-pack",
18-
"vmware.vscode-spring-boot",
18+
"vmware.vscode-boot-dev-pack",
1919
"GitHub.copilot",
2020
"GitHub.vscode-github-actions"
2121
]
@@ -26,8 +26,8 @@
2626
],
2727
"postCreateCommand": "cd ./sample-app && mvn dependency:resolve --quiet",
2828
"hostRequirements": {
29-
"memory": "8gb",
30-
"cpus": 4
29+
"memory": "4gb",
30+
"cpus": 2
3131
},
3232
"portsAttributes": {
3333
"8080": {

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ jobs:
2525
cache: 'maven'
2626

2727
- name: Build
28-
run: mvn compile -f sample-app/pom.xml
28+
run: mvn verify -f sample-app/pom.xml

.vscode/launch.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
2-
"version": "0.2.0",
32
"configurations": [
43
{
5-
"name": "SampleApp",
64
"type": "java",
5+
"name": "Launch Java Program",
76
"request": "launch",
87
"mainClass": "com.example.app.Application",
9-
"projectName": "sample-app",
10-
"cwd": "${workspaceFolder}/sample-app"
8+
"vmArgs": "--add-modules jdk.httpserver",
9+
"classPaths": [
10+
"${workspaceFolder}/sample-app/target/classes"
11+
]
1112
}
1213
]
13-
}
14+
}

.vscode/tasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"type": "shell",
77
"command": "mvn",
88
"args": [
9-
"compile",
9+
"verify",
1010
"-f",
1111
"${workspaceFolder}/sample-app/pom.xml"
1212
],

README.md

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,50 @@
11
# GitHub Codespaces ♥️ Java
22

3-
Want to try out Java for web development?
3+
This repository is a weather app built on the JDK 25 built-in HTTP server.
44

5-
This repo builds a Weather App with a REST API (with OpenAPI/Swagger UI) and a web frontend, all in a single Spring Boot application using Thymeleaf.
5+
It serves:
6+
- a REST API at `/api/weatherforecast`
7+
- static frontend assets (`index.html`, CSS, JavaScript)
68

7-
Everything you do here is contained within this one codespace. There is no repository on GitHub yet. If and when you're ready you can click "Publish Branch" and we'll create your repository and push up your project. If you were just exploring then and have no further need for this code then you can simply delete your codespace and it's gone forever.
9+
## Run
810

9-
### Run Options
11+
1. Compile:
1012

11-
[![Open in GitHub Codespaces](https://img.shields.io/static/v1?style=for-the-badge&label=GitHub+Codespaces&message=Open&color=lightgrey&logo=github)](https://codespaces.new/github/java-codespaces)
12-
[![Open in Dev Container](https://img.shields.io/static/v1?style=for-the-badge&label=Dev+Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/github/java-codespaces)
13+
mvn compile -f sample-app/pom.xml
1314

14-
You can also run this repository locally by following these instructions:
15-
1. Clone the repo to your local machine `git clone https://github.com/github/java-codespaces`
16-
1. Open repo in VS Code
15+
2. Start:
1716

18-
## Getting started
17+
java --module-path sample-app/target/classes --module com.example.app/com.example.app.Application
1918

20-
1. **📤 One-click setup**: [Open a new Codespace](https://codespaces.new/github/java-codespaces), giving you a fully configured cloud developer environment.
21-
2. **▶️ Run all, one-click again**: Use VS Code's built-in *Run* command and open the forwarded port *8080* in your browser.
19+
3. Open in browser:
2220

23-
3. The web app will be available on port **8080**. Navigate to **/swagger-ui** for interactive API documentation.
21+
http://localhost:8080
2422

25-
4. **🔄 Iterate quickly:** Codespaces updates the server on each save, and VS Code's debugger lets you dig into the code execution.
23+
## API
2624

27-
5. To stop running, return to VS Code, and click Stop in the debug toolbar.
25+
- `GET /api/weatherforecast` returns a 5-day forecast JSON payload.
2826

2927
## Project Structure
3028

3129
```
3230
sample-app/
33-
├── pom.xml # Maven project
31+
├── pom.xml
3432
└── src/main/
35-
├── java/com/example/app/
36-
│ ├── Application.java
37-
│ ├── WeatherForecast.java
38-
│ ├── WeatherForecastController.java
39-
│ └── WeatherController.java
40-
└── resources/
41-
├── application.properties
42-
├── templates/
43-
│ ├── index.html
44-
│ └── error.html
45-
└── static/css/site.css
33+
├── java/com/example/app/
34+
│ ├── Application.java
35+
│ ├── StaticFileHandler.java
36+
│ ├── WeatherForecast.java
37+
│ ├── WeatherForecastService.java
38+
│ └── WeatherHttpServer.java
39+
└── resources/static/
40+
├── index.html
41+
├── css/site.css
42+
└── js/app.js
4643
```
4744

4845
## Tech Stack
4946

50-
- **Java 25** with Spring Boot 3.5
51-
- **API**: Spring Web + springdoc-openapi (Swagger UI)
52-
- **Web UI**: Thymeleaf + Bootstrap 5
53-
- **Build**: Maven
47+
- Java 25
48+
- JDK `HttpServer` (`jdk.httpserver` module)
49+
- Vanilla JavaScript + Bootstrap 5
50+
- Apache Maven
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
wrapperVersion=3.3.4
22
distributionType=only-script
3-
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
3+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip

sample-app/pom.xml

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,42 @@
44
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
55
<modelVersion>4.0.0</modelVersion>
66

7-
<parent>
8-
<groupId>org.springframework.boot</groupId>
9-
<artifactId>spring-boot-starter-parent</artifactId>
10-
<version>3.5.9</version>
11-
<relativePath/>
12-
</parent>
13-
147
<groupId>com.example</groupId>
158
<artifactId>sample-app</artifactId>
169
<version>0.0.1-SNAPSHOT</version>
1710
<name>SampleApp</name>
18-
<description>Weather Forecast Sample App - Java version of dotnet-codespaces</description>
11+
<description>Java weather app using JDK HTTP server</description>
1912

2013
<properties>
21-
<java.version>25</java.version>
14+
<maven.compiler.release>25</maven.compiler.release>
15+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2216
</properties>
2317

24-
<dependencies>
25-
<dependency>
26-
<groupId>org.springframework.boot</groupId>
27-
<artifactId>spring-boot-starter-web</artifactId>
28-
</dependency>
29-
<dependency>
30-
<groupId>org.springframework.boot</groupId>
31-
<artifactId>spring-boot-starter-thymeleaf</artifactId>
32-
</dependency>
33-
<dependency>
34-
<groupId>org.springdoc</groupId>
35-
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
36-
<version>2.8.6</version>
37-
</dependency>
38-
</dependencies>
39-
4018
<build>
4119
<plugins>
4220
<plugin>
43-
<groupId>org.springframework.boot</groupId>
44-
<artifactId>spring-boot-maven-plugin</artifactId>
21+
<groupId>org.apache.maven.plugins</groupId>
22+
<artifactId>maven-compiler-plugin</artifactId>
23+
<version>3.15.0</version>
24+
<configuration>
25+
<release>${maven.compiler.release}</release>
26+
<compilerArgs>
27+
<arg>--add-modules</arg>
28+
<arg>jdk.httpserver</arg>
29+
</compilerArgs>
30+
</configuration>
31+
</plugin>
32+
<plugin>
33+
<groupId>org.apache.maven.plugins</groupId>
34+
<artifactId>maven-jar-plugin</artifactId>
35+
<version>3.5.0</version>
36+
<configuration>
37+
<archive>
38+
<manifest>
39+
<mainClass>com.example.app.Application</mainClass>
40+
</manifest>
41+
</archive>
42+
</configuration>
4543
</plugin>
4644
</plugins>
4745
</build>
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
package com.example.app;
22

3-
import org.springframework.boot.SpringApplication;
4-
import org.springframework.boot.autoconfigure.SpringBootApplication;
3+
import java.io.IOException;
54

6-
@SpringBootApplication
75
public class Application {
86

9-
public static void main(String[] args) {
10-
SpringApplication.run(Application.class, args);
7+
private static final int DEFAULT_PORT = 8080;
8+
9+
public static void main(String[] args) throws IOException {
10+
int port = resolvePort();
11+
12+
var forecastController = new WeatherForecastService();
13+
var server = new WeatherHttpServer(port, forecastController);
14+
15+
server.start();
16+
}
17+
18+
private static int resolvePort() {
19+
String portValue = System.getenv("PORT");
20+
if (portValue == null || portValue.isBlank()) {
21+
return DEFAULT_PORT;
22+
}
23+
24+
try {
25+
return Integer.parseInt(portValue);
26+
} catch (NumberFormatException ignored) {
27+
return DEFAULT_PORT;
28+
}
1129
}
1230
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.example.app;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.nio.charset.StandardCharsets;
6+
import java.util.Map;
7+
8+
import com.sun.net.httpserver.HttpExchange;
9+
import com.sun.net.httpserver.HttpHandler;
10+
11+
public class StaticFileHandler implements HttpHandler {
12+
13+
private static final Map<String, String> MIME_TYPES = Map.of(
14+
"html", "text/html; charset=utf-8",
15+
"css", "text/css; charset=utf-8",
16+
"js", "application/javascript; charset=utf-8",
17+
"json", "application/json; charset=utf-8",
18+
"png", "image/png",
19+
"svg", "image/svg+xml",
20+
"ico", "image/x-icon");
21+
22+
@Override
23+
public void handle(HttpExchange exchange) throws IOException {
24+
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
25+
sendText(exchange, 405, "Method Not Allowed");
26+
return;
27+
}
28+
29+
var requestUri = exchange.getRequestURI();
30+
var rawPath = requestUri.getPath();
31+
var path = (rawPath == null || "/".equals(rawPath)) ? "/index.html" : rawPath;
32+
33+
if (path.contains("..")) {
34+
sendText(exchange, 400, "Bad Request");
35+
return;
36+
}
37+
38+
var resourcePath = "/static" + path;
39+
try (InputStream stream = StaticFileHandler.class.getResourceAsStream(resourcePath)) {
40+
if (stream == null) {
41+
sendText(exchange, 404, "Not Found");
42+
return;
43+
}
44+
45+
var bytes = stream.readAllBytes();
46+
var headers = exchange.getResponseHeaders();
47+
headers.set("Content-Type", resolveMimeType(path));
48+
exchange.sendResponseHeaders(200, bytes.length);
49+
try (var output = exchange.getResponseBody()) {
50+
output.write(bytes);
51+
}
52+
}
53+
}
54+
55+
private static void sendText(HttpExchange exchange, int statusCode, String payload) throws IOException {
56+
var bytes = payload.getBytes(StandardCharsets.UTF_8);
57+
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
58+
exchange.sendResponseHeaders(statusCode, bytes.length);
59+
try (var responseBody = exchange.getResponseBody()) {
60+
responseBody.write(bytes);
61+
}
62+
}
63+
64+
private static String resolveMimeType(String path) {
65+
int extensionIndex = path.lastIndexOf('.');
66+
if (extensionIndex < 0 || extensionIndex == path.length() - 1) {
67+
return "application/octet-stream";
68+
}
69+
var extension = path.substring(extensionIndex + 1).toLowerCase();
70+
return MIME_TYPES.getOrDefault(extension, "application/octet-stream");
71+
}
72+
}

sample-app/src/main/java/com/example/app/WeatherController.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)