- Implemented WebSocketSession abstract class with callback-based API - Added ServletWebSocketSession with full Jakarta WebSocket bridging - Created @WebSocket annotation for declarative endpoint marking - Updated JettyApp to initialize Jakarta WebSocket container - Split Request/Response into abstract base and servlet implementations - Moved JettyApp to jabba.servlet package - Moved annotations to jabba.decor package - Added comprehensive WebSocket test suite (5 tests, all passing) - Updated README.md with WebSocket documentation and examples - All 31 tests passing (async, sync, security, websocket, database) - Fixed spelling errors in README.md
142 lines
3.9 KiB
Groovy
142 lines
3.9 KiB
Groovy
/** we define some extra tasks to be imported into main build or shared. */
|
|
|
|
// load .env settings - mostly secrets
|
|
task dotenv{
|
|
def ef=file('.env');
|
|
if(!ef.exists()) ef=file('../.env');
|
|
ef.readLines().each() {
|
|
if(it.isEmpty() || it.startsWith("#")) return true;
|
|
def (key,value)=it.tokenize('=')
|
|
project.ext.set(key,value)
|
|
}
|
|
}
|
|
task packageJavadoc(type: Jar, dependsOn: 'javadoc') {
|
|
from javadoc
|
|
archiveClassifier = 'javadoc'
|
|
}
|
|
task packageSources(type: Jar, dependsOn: 'classes') {
|
|
from sourceSets.main.allSource
|
|
archiveClassifier = 'sources'
|
|
}
|
|
task fat_jar(type: Jar) {
|
|
archiveBaseName = 'fat-'+project.name
|
|
archiveVersion = project.version
|
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
/*
|
|
manifest {
|
|
attributes "Main-Class": mainClassName
|
|
attributes "Class-Path": configurations.runtimeClasspath.collect { it.getName() }.join(' ')
|
|
}
|
|
*/
|
|
from {
|
|
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
|
}{
|
|
exclude "META-INF/NOTICE.txt"
|
|
exclude "META-INF/LICENSE"
|
|
}
|
|
with jar
|
|
}
|
|
|
|
/**
|
|
We define a singleton pattern using background thread to launch a blocking process.
|
|
Later we make sure to terminate previous running driver threads before starting new.
|
|
Also we split start, stop because we might not run in continouse mode.
|
|
*/
|
|
class Server implements Runnable{
|
|
static Server singleton=null;
|
|
public static Server main(){
|
|
if(singleton==null) singleton=new Server();
|
|
return singleton;
|
|
}
|
|
//org.slf4j.Logger log=null;
|
|
Thread driver=null;
|
|
Runnable task=null;
|
|
protected Server(){}
|
|
public void info(String msg){
|
|
//if(log!=null) log.info(msg); else println(msg);
|
|
println(msg);
|
|
}
|
|
//public Server setLogger(org.slf4j.Logger l){
|
|
// log=l;
|
|
// return this;
|
|
//}
|
|
public Server useDriver(boolean f){
|
|
info("using driver:"+f);
|
|
if(f){
|
|
driver=new Thread(this);
|
|
driver.setName("server.driver");
|
|
}else{
|
|
driver=null;
|
|
}
|
|
return this;
|
|
}
|
|
public void run(){
|
|
info("running task");
|
|
try{
|
|
task.run();
|
|
}catch(java.lang.Exception ex){
|
|
info("running task:interrupted");
|
|
}
|
|
}
|
|
public Server start(Runnable c){
|
|
info("starting server");
|
|
this.task=c;
|
|
if(driver!=null){
|
|
driver.start();
|
|
}else{
|
|
this.run();
|
|
}
|
|
return this;
|
|
}
|
|
public Server stop(){
|
|
info("stopping server");
|
|
if(driver!=null){
|
|
driver.interrupt();
|
|
try{
|
|
driver.join(5000); // Wait up to 5 seconds for graceful shutdown
|
|
}catch(InterruptedException e){
|
|
info("interrupted while waiting for driver to stop");
|
|
}
|
|
}
|
|
// Clean up stale threads using proper interruption
|
|
for(Thread th:Thread.getAllStackTraces().keySet()){
|
|
if(th.getName().equalsIgnoreCase("executor")){
|
|
info("cleaning up stale driver:"+th.toString())
|
|
th.interrupt();
|
|
try{
|
|
th.join(2000); // Wait up to 2 seconds
|
|
}catch(InterruptedException e){
|
|
// Ignore
|
|
}
|
|
}
|
|
if(th.getName().equalsIgnoreCase("server.driver")){
|
|
info("cleaning up stale driver:"+th.toString())
|
|
th.interrupt();
|
|
try{
|
|
th.join(2000); // Wait up to 2 seconds
|
|
}catch(InterruptedException e){
|
|
// Ignore
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
task runServer{
|
|
inputs.files 'src'
|
|
doFirst {
|
|
boolean threaded=project.gradle.startParameter.continuous;
|
|
Server.main().stop().useDriver(threaded);//.setLogger(logger);
|
|
}
|
|
doLast {
|
|
Server.main().start({
|
|
println(application.mainClass.get())
|
|
project.javaexec {
|
|
classpath = sourceSets.main.runtimeClasspath
|
|
main = application.mainClass.get()
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|