Skip to content

Commit 3140f8a

Browse files
committed
feature: add Jetty server bootstrap for license validation
Adds embedded Jetty 12 server with: - jetty-server 12.1.5 (core only, no servlets) - Jackson 3.0.3 for JSON processing - SLF4J 2.0.17 for logging - CatLicenseServer main class with Handler.Abstract - Health endpoint at /health using Jetty Core API - Configurable port via server-config.properties Task ID: v2.0-license-validation-server-jetty-setup
1 parent d2d7140 commit 3140f8a

5 files changed

Lines changed: 205 additions & 3 deletions

File tree

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# State
22

3-
- **Status:** pending
4-
- **Progress:** 0%
3+
- **Status:** completed
4+
- **Progress:** 100%
5+
- **Resolution:** implemented
56
- **Dependencies:** [jwt-token-generation, tier-feature-mapping]
6-
- **Last Updated:** 2026-01-23
7+
- **Completed:** 2026-01-24 12:30
8+
- **Tokens Used:** ~25,000
9+
- **Last Updated:** 2026-01-24

.claude/rules/java-style.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Java Style Conventions
2+
3+
## String Comparisons
4+
5+
Use `Objects.equals(variable, "literal")` instead of `"literal".equals(variable)`.
6+
7+
```java
8+
// Preferred
9+
if (Objects.equals(path, "/health"))
10+
11+
// Avoid
12+
if ("/health".equals(path))
13+
```

server/pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<maven.compiler.target>21</maven.compiler.target>
1818
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1919
<testng.version>7.9.0</testng.version>
20+
<jetty.version>12.1.5</jetty.version>
21+
<slf4j.version>2.0.17</slf4j.version>
22+
<jackson.version>3.0.3</jackson.version>
2023
</properties>
2124

2225
<dependencies>
@@ -26,6 +29,24 @@
2629
<version>${testng.version}</version>
2730
<scope>test</scope>
2831
</dependency>
32+
<!-- Jetty 12 Server (core) -->
33+
<dependency>
34+
<groupId>org.eclipse.jetty</groupId>
35+
<artifactId>jetty-server</artifactId>
36+
<version>${jetty.version}</version>
37+
</dependency>
38+
<!-- JSON processing (Jackson 3.x) -->
39+
<dependency>
40+
<groupId>tools.jackson.core</groupId>
41+
<artifactId>jackson-databind</artifactId>
42+
<version>${jackson.version}</version>
43+
</dependency>
44+
<!-- SLF4J for Jetty logging -->
45+
<dependency>
46+
<groupId>org.slf4j</groupId>
47+
<artifactId>slf4j-simple</artifactId>
48+
<version>${slf4j.version}</version>
49+
</dependency>
2950
</dependencies>
3051

3152
<build>
@@ -47,6 +68,18 @@
4768
<artifactId>maven-surefire-plugin</artifactId>
4869
<version>3.2.5</version>
4970
</plugin>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-jar-plugin</artifactId>
74+
<version>3.3.0</version>
75+
<configuration>
76+
<archive>
77+
<manifest>
78+
<mainClass>io.github.cowwoc.claudecodecat.CatLicenseServer</mainClass>
79+
</manifest>
80+
</archive>
81+
</configuration>
82+
</plugin>
5083
</plugins>
5184
</build>
5285
</project>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package io.github.cowwoc.claudecodecat;
2+
3+
import org.eclipse.jetty.io.Content;
4+
import org.eclipse.jetty.server.Handler;
5+
import org.eclipse.jetty.server.Request;
6+
import org.eclipse.jetty.server.Response;
7+
import org.eclipse.jetty.server.Server;
8+
import org.eclipse.jetty.util.Callback;
9+
10+
import java.io.IOException;
11+
import java.io.InputStream;
12+
import java.util.Objects;
13+
import java.util.Properties;
14+
15+
/**
16+
* Main entry point for the CAT License Server.
17+
* Embedded Jetty server providing license validation endpoints.
18+
*/
19+
public class CatLicenseServer
20+
{
21+
private final int port;
22+
private Server server;
23+
24+
/**
25+
* Creates a new license server instance.
26+
*
27+
* @param port the port to listen on
28+
*/
29+
public CatLicenseServer(int port)
30+
{
31+
this.port = port;
32+
}
33+
34+
/**
35+
* Starts the server.
36+
*
37+
* @throws Exception if the server fails to start
38+
*/
39+
public void start() throws Exception
40+
{
41+
server = new Server(port);
42+
43+
// Use Jetty Core Handler API for routing
44+
Handler handler = new Handler.Abstract()
45+
{
46+
@Override
47+
public boolean handle(Request request, Response response, Callback callback) throws Exception
48+
{
49+
String path = request.getHttpURI().getPath();
50+
51+
if (Objects.equals(path, "/health"))
52+
{
53+
response.setStatus(200);
54+
response.getHeaders().put("Content-Type", "application/json");
55+
Content.Sink.write(response, true, "{\"status\":\"healthy\"}", callback);
56+
return true;
57+
}
58+
59+
// Not handled - return 404
60+
return false;
61+
}
62+
};
63+
64+
server.setHandler(handler);
65+
server.start();
66+
System.out.println("CAT License Server started on port " + port);
67+
}
68+
69+
/**
70+
* Stops the server.
71+
*
72+
* @throws Exception if the server fails to stop
73+
*/
74+
public void stop() throws Exception
75+
{
76+
if (server != null)
77+
{
78+
server.stop();
79+
}
80+
}
81+
82+
/**
83+
* Blocks until the server is terminated.
84+
*
85+
* @throws InterruptedException if interrupted while waiting
86+
*/
87+
public void join() throws InterruptedException
88+
{
89+
if (server != null)
90+
{
91+
server.join();
92+
}
93+
}
94+
95+
/**
96+
* Returns the port the server is listening on.
97+
*
98+
* @return the server port
99+
*/
100+
public int getPort()
101+
{
102+
return port;
103+
}
104+
105+
/**
106+
* Main entry point.
107+
*
108+
* @param args command line arguments (unused)
109+
* @throws Exception if the server fails to start
110+
*/
111+
public static void main(String[] args) throws Exception
112+
{
113+
Properties props = loadConfig();
114+
int port = Integer.parseInt(props.getProperty("server.port", "8080"));
115+
116+
CatLicenseServer server = new CatLicenseServer(port);
117+
server.start();
118+
119+
// Add shutdown hook for graceful shutdown
120+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
121+
try
122+
{
123+
server.stop();
124+
}
125+
catch (Exception e)
126+
{
127+
e.printStackTrace();
128+
}
129+
}));
130+
131+
server.join();
132+
}
133+
134+
private static Properties loadConfig()
135+
{
136+
Properties props = new Properties();
137+
try (InputStream input = CatLicenseServer.class.getClassLoader()
138+
.getResourceAsStream("server-config.properties"))
139+
{
140+
if (input != null)
141+
{
142+
props.load(input);
143+
}
144+
}
145+
catch (IOException e)
146+
{
147+
System.err.println("Warning: Could not load server-config.properties, using defaults");
148+
}
149+
return props;
150+
}
151+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# CAT License Server Configuration
2+
server.port=8080

0 commit comments

Comments
 (0)