1919package de .rwth .idsg .steve ;
2020
2121import lombok .extern .slf4j .Slf4j ;
22+ import org .eclipse .jetty .ee10 .webapp .WebAppContext ;
2223import org .eclipse .jetty .http .HttpScheme ;
2324import org .eclipse .jetty .http .HttpVersion ;
24- import org .eclipse .jetty .server .Connector ;
2525import org .eclipse .jetty .server .ForwardedRequestCustomizer ;
26+ import org .eclipse .jetty .server .Handler ;
2627import org .eclipse .jetty .server .HttpConfiguration ;
2728import org .eclipse .jetty .server .HttpConnectionFactory ;
2829import org .eclipse .jetty .server .SecureRequestCustomizer ;
2930import org .eclipse .jetty .server .Server ;
3031import org .eclipse .jetty .server .ServerConnector ;
3132import org .eclipse .jetty .server .SslConnectionFactory ;
33+ import org .eclipse .jetty .server .handler .gzip .GzipHandler ;
3234import org .eclipse .jetty .util .ssl .SslContextFactory ;
3335import org .eclipse .jetty .util .thread .QueuedThreadPool ;
3436import org .eclipse .jetty .util .thread .ScheduledExecutorScheduler ;
37+ import org .springframework .core .io .ClassPathResource ;
3538
36- import java .net .DatagramSocket ;
37- import java .net .Inet4Address ;
38- import java .net .InetAddress ;
39- import java .net .InetSocketAddress ;
40- import java .net .NetworkInterface ;
41- import java .net .Socket ;
42- import java .util .ArrayList ;
43- import java .util .Arrays ;
44- import java .util .Collection ;
45- import java .util .Collections ;
46- import java .util .Enumeration ;
47- import java .util .HashSet ;
48- import java .util .List ;
49- import java .util .Set ;
39+ import java .io .IOException ;
5040import java .util .concurrent .TimeUnit ;
51- import java .util .stream .Collectors ;
5241
5342import static de .rwth .idsg .steve .SteveConfiguration .CONFIG ;
5443
6049public class JettyServer {
6150
6251 private Server server ;
63- private SteveAppContext steveAppContext ;
6452
6553 private static final int MIN_THREADS = 4 ;
6654 private static final int MAX_THREADS = 50 ;
6755
6856 private static final long STOP_TIMEOUT = TimeUnit .SECONDS .toMillis (5 );
6957 private static final long IDLE_TIMEOUT = TimeUnit .MINUTES .toMillis (1 );
7058
59+ // scan all jars in the classpath for ServletContainerInitializers
60+ // (e.g. Spring's WebApplicationInitializer)
61+ private static final String SCAN_PATTERN = ".*\\ .jar$|.*/classes/.*" ;
62+
7163 /**
7264 * A fully configured Jetty Server instance
7365 */
74- private void prepare () {
66+ private void prepare () throws IOException {
7567
7668 // === jetty.xml ===
7769 // Setup Threadpool
@@ -114,8 +106,7 @@ private void prepare() {
114106 server .addConnector (httpsConnector (httpConfig ));
115107 }
116108
117- steveAppContext = new SteveAppContext ();
118- server .setHandler (steveAppContext .getHandlers ());
109+ server .setHandler (getWebApp ());
119110 }
120111
121112 private ServerConnector httpConnector (HttpConfiguration httpConfig ) {
@@ -149,6 +140,35 @@ private ServerConnector httpsConnector(HttpConfiguration httpConfig) {
149140 return https ;
150141 }
151142
143+ private Handler getWebApp () throws IOException {
144+ var webAppContext = new WebAppContext ();
145+ webAppContext .setContextPath (CONFIG .getContextPath ());
146+ webAppContext .setBaseResourceAsString (new ClassPathResource ("webapp" ).getURI ().toString ());
147+
148+ // if during startup an exception happens, do not swallow it, throw it
149+ webAppContext .setThrowUnavailableOnStartupException (true );
150+
151+ // Disable directory listings if no index.html is found.
152+ webAppContext .setInitParameter ("org.eclipse.jetty.servlet.Default.dirAllowed" , "false" );
153+
154+ // Crucial for Spring's WebApplicationInitializer to be discovered
155+ // and for the DispatcherServlet to be initialized.
156+ // It tells Jetty to scan for classes implementing WebApplicationInitializer.
157+ // The pattern ensures that Jetty finds the Spring classes in the classpath.
158+ //
159+ // https://jetty.org/docs/jetty/12.1/programming-guide/maven-jetty/jetty-maven-plugin.html
160+ // https://jetty.org/docs/jetty/12.1/operations-guide/annotations/index.html#og-container-include-jar-pattern
161+ webAppContext .setAttribute ("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern" , SCAN_PATTERN );
162+
163+ if (CONFIG .getJetty ().isGzipEnabled ()) {
164+ // Wraps the whole web app in a gzip handler to make Jetty return compressed content
165+ // http://www.eclipse.org/jetty/documentation/current/gzip-filter.html
166+ return new GzipHandler (webAppContext );
167+ } else {
168+ return webAppContext ;
169+ }
170+ }
171+
152172 /**
153173 * Starts the Jetty Server instance
154174 */
@@ -157,8 +177,6 @@ public void start() throws Exception {
157177
158178 if (server != null ) {
159179 server .start ();
160- steveAppContext .configureWebSocket ();
161- populateEndpointInfo ();
162180 }
163181 }
164182
@@ -176,124 +194,4 @@ public void stop() throws Exception {
176194 server .stop ();
177195 }
178196 }
179-
180- public boolean isStarted () {
181- return server != null && server .isStarted ();
182- }
183-
184- public void populateEndpointInfo () {
185- List <String > list = getConnectorPathList ();
186-
187- // EndpointInfo info = EndpointInfo.INSTANCE;
188- //
189- // info.getWebInterface().setData(buildList(list, false));
190- // info.getOcppSoap().setData(buildList(list, false));
191- // info.getOcppWebSocket().setData(buildList(list, true));
192- }
193-
194- private List <String > getConnectorPathList () {
195- if (server == null ) {
196- return Collections .emptyList ();
197- }
198-
199- return Arrays .stream (server .getConnectors ())
200- .map (JettyServer ::getConnectorPath )
201- .flatMap (Collection ::stream )
202- .collect (Collectors .toList ());
203- }
204-
205- private static List <String > getConnectorPath (Connector c ) {
206- ServerConnector sc = (ServerConnector ) c ;
207-
208- final String prefix ;
209- if (sc .getDefaultConnectionFactory () instanceof SslConnectionFactory ) {
210- prefix = "https" ;
211- } else {
212- prefix = "http" ;
213- }
214-
215- Set <String > ips = new HashSet <>();
216- String host = sc .getHost ();
217- if (host == null || host .equals ("0.0.0.0" )) {
218- ips .addAll (getPossibleIpAddresses ());
219- } else {
220- ips .add (host );
221- }
222-
223- String layout = "%s://%s:%d" + CONFIG .getContextPath ();
224-
225- return ips .stream ()
226- .map (k -> String .format (layout , prefix , k , sc .getPort ()))
227- .collect (Collectors .toList ());
228- }
229-
230- private List <String > buildList (List <String > list , boolean replaceHttp ) {
231- return list .stream ()
232- .map (s -> getElementPrefix (s , replaceHttp ))
233- .collect (Collectors .toList ());
234- }
235-
236- private String getElementPrefix (String str , boolean replaceHttp ) {
237- if (replaceHttp ) {
238- return str .replaceFirst ("http" , "ws" );
239- } else {
240- return str ;
241- }
242- }
243-
244- /**
245- * Uses different APIs to find out the IP of this machine.
246- */
247- private static List <String > getPossibleIpAddresses () {
248- final String host = "treibhaus.informatik.rwth-aachen.de" ;
249- final List <String > ips = new ArrayList <>();
250-
251- try {
252- ips .add (InetAddress .getLocalHost ().getHostAddress ());
253- } catch (Exception e ) {
254- // fail silently
255- }
256-
257- try {
258- Socket socket = new Socket ();
259- socket .connect (new InetSocketAddress (host , 80 ));
260- ips .add (socket .getLocalAddress ().getHostAddress ());
261- } catch (Exception e ) {
262- // fail silently
263- }
264-
265- // https://stackoverflow.com/a/38342964
266- try (DatagramSocket socket = new DatagramSocket ()) {
267- socket .connect (InetAddress .getByName ("8.8.8.8" ), 10002 );
268- ips .add (socket .getLocalAddress ().getHostAddress ());
269- } catch (Exception e ) {
270- // fail silently
271- }
272-
273- // https://stackoverflow.com/a/20418809
274- try {
275- for (Enumeration <NetworkInterface > ifaces = NetworkInterface .getNetworkInterfaces (); ifaces .hasMoreElements ();) {
276- NetworkInterface iface = ifaces .nextElement ();
277- for (Enumeration <InetAddress > inetAddrs = iface .getInetAddresses (); inetAddrs .hasMoreElements ();) {
278- InetAddress inetAddr = inetAddrs .nextElement ();
279- if (!inetAddr .isLoopbackAddress () && (inetAddr instanceof Inet4Address )) {
280- ips .add (inetAddr .getHostAddress ());
281- }
282- }
283- }
284-
285- } catch (Exception e ) {
286- // fail silently
287- }
288-
289- ips .removeIf ("0.0.0.0" ::equals );
290-
291- if (ips .isEmpty ()) {
292- // Well, we failed to read from system, fall back to application.properties.
293- // Better than nothing
294- ips .add (CONFIG .getJetty ().getServerHost ());
295- }
296-
297- return ips ;
298- }
299197}
0 commit comments