1515import com .sun .net .httpserver .HttpServer ;
1616import java .io .IOException ;
1717import java .net .InetSocketAddress ;
18+ import java .util .LinkedHashMap ;
1819import java .util .Map ;
1920import java .util .Optional ;
2021import org .slf4j .Logger ;
@@ -45,7 +46,7 @@ public OpenApiServer(
4546 Map <String , HttpHandler > handlers ,
4647 ExceptionHandler exceptionHandler )
4748 throws IOException {
48- this (spec , jsonMapper , handlers , exceptionHandler , DEFAULT_PORT );
49+ this (spec , jsonMapper , handlers , exceptionHandler , DEFAULT_PORT , Map . of () );
4950 }
5051
5152 /**
@@ -63,6 +64,17 @@ public OpenApiServer(
6364 ExceptionHandler exceptionHandler ,
6465 int port )
6566 throws IOException {
67+ this (spec , jsonMapper , handlers , exceptionHandler , port , Map .of ());
68+ }
69+
70+ OpenApiServer (
71+ Spec spec ,
72+ JsonMapper jsonMapper ,
73+ Map <String , HttpHandler > handlers ,
74+ ExceptionHandler exceptionHandler ,
75+ int port ,
76+ Map <String , HttpHandler > extras )
77+ throws IOException {
6678
6779 requireNonNull (spec , "Spec must not be null" );
6880 requireNonNull (jsonMapper , "JsonMapper must not be null" );
@@ -84,6 +96,12 @@ public OpenApiServer(
8496 ctx .getFilters ().add (new RequestPreparationFilter (spec , router , validator , jsonMapper ));
8597 ctx .setHandler (new DispatchHandler (handlers ));
8698
99+ for (Map .Entry <String , HttpHandler > e : extras .entrySet ()) {
100+ HttpContext extraCtx = httpServer .createContext (e .getKey ());
101+ extraCtx .getFilters ().add (new ExceptionFilter (exceptionHandler ));
102+ extraCtx .setHandler (e .getValue ());
103+ }
104+
87105 httpServer .createContext ("/" , Handlers .notFoundHandler ());
88106 httpServer .start ();
89107
@@ -100,4 +118,70 @@ public void close() {
100118 httpServer .stop (0 );
101119 }
102120 }
121+
122+ public static Builder builder () {
123+ return new Builder ();
124+ }
125+
126+ /** Fluent builder for {@link OpenApiServer}. */
127+ public static final class Builder {
128+
129+ private Spec spec ;
130+ private JsonMapper jsonMapper ;
131+ private Map <String , HttpHandler > handlers ;
132+ private ExceptionHandler exceptionHandler ;
133+ private int port = DEFAULT_PORT ;
134+ private final LinkedHashMap <String , HttpHandler > extras = new LinkedHashMap <>();
135+
136+ private Builder () {}
137+
138+ public Builder spec (Spec spec ) {
139+ this .spec = spec ;
140+ return this ;
141+ }
142+
143+ public Builder jsonMapper (JsonMapper jsonMapper ) {
144+ this .jsonMapper = jsonMapper ;
145+ return this ;
146+ }
147+
148+ public Builder handlers (Map <String , HttpHandler > handlers ) {
149+ this .handlers = handlers ;
150+ return this ;
151+ }
152+
153+ public Builder exceptionHandler (ExceptionHandler exceptionHandler ) {
154+ this .exceptionHandler = exceptionHandler ;
155+ return this ;
156+ }
157+
158+ public Builder port (int port ) {
159+ this .port = port ;
160+ return this ;
161+ }
162+
163+ public Builder addHandler (String path , HttpHandler handler ) {
164+ requireNonNull (path , "path must not be null" );
165+ requireNonNull (handler , "handler must not be null" );
166+ if (extras .containsKey (path )) {
167+ throw new IllegalStateException ("duplicate extra handler path: " + path );
168+ }
169+ extras .put (path , handler );
170+ return this ;
171+ }
172+
173+ public OpenApiServer build () throws IOException {
174+ requireNonNull (spec , "Spec must not be null" );
175+ requireNonNull (jsonMapper , "JsonMapper must not be null" );
176+ requireNonNull (handlers , "handlers must not be null" );
177+ String basePath = Optional .ofNullable (spec .basePath ()).orElse ("/" );
178+ for (String path : extras .keySet ()) {
179+ if (path .equals (basePath )) {
180+ throw new IllegalStateException (
181+ "extra handler path " + path + " conflicts with spec basePath " + basePath );
182+ }
183+ }
184+ return new OpenApiServer (spec , jsonMapper , handlers , exceptionHandler , port , extras );
185+ }
186+ }
103187}
0 commit comments