1 package org
.argeo
.cms
.jetty
;
3 import java
.io
.IOException
;
4 import java
.net
.InetSocketAddress
;
6 import java
.util
.TreeMap
;
7 import java
.util
.concurrent
.Executor
;
8 import java
.util
.concurrent
.ThreadPoolExecutor
;
10 import javax
.servlet
.ServletException
;
12 import org
.argeo
.api
.cms
.CmsLog
;
13 import org
.argeo
.api
.cms
.CmsState
;
14 import org
.argeo
.cms
.CmsDeployProperty
;
15 import org
.argeo
.util
.http
.HttpServerUtils
;
16 import org
.eclipse
.jetty
.http
.UriCompliance
;
17 import org
.eclipse
.jetty
.server
.HttpConfiguration
;
18 import org
.eclipse
.jetty
.server
.HttpConnectionFactory
;
19 import org
.eclipse
.jetty
.server
.SecureRequestCustomizer
;
20 import org
.eclipse
.jetty
.server
.Server
;
21 import org
.eclipse
.jetty
.server
.ServerConnector
;
22 import org
.eclipse
.jetty
.server
.SslConnectionFactory
;
23 import org
.eclipse
.jetty
.server
.handler
.ContextHandlerCollection
;
24 import org
.eclipse
.jetty
.servlet
.ServletContextHandler
;
25 import org
.eclipse
.jetty
.util
.ssl
.SslContextFactory
;
26 import org
.eclipse
.jetty
.util
.thread
.ExecutorThreadPool
;
27 import org
.eclipse
.jetty
.util
.thread
.QueuedThreadPool
;
28 import org
.eclipse
.jetty
.util
.thread
.ThreadPool
;
30 import com
.sun
.net
.httpserver
.HttpContext
;
31 import com
.sun
.net
.httpserver
.HttpHandler
;
32 import com
.sun
.net
.httpserver
.HttpsConfigurator
;
33 import com
.sun
.net
.httpserver
.HttpsServer
;
35 public class JettyHttpServer
extends HttpsServer
{
36 private final static CmsLog log
= CmsLog
.getLog(JettyHttpServer
.class);
38 private static final int DEFAULT_IDLE_TIMEOUT
= 30000;
40 private Server server
;
42 protected ServerConnector httpConnector
;
43 protected ServerConnector httpsConnector
;
45 private InetSocketAddress httpAddress
;
46 private InetSocketAddress httpsAddress
;
48 private ThreadPoolExecutor executor
;
50 private HttpsConfigurator httpsConfigurator
;
52 private final Map
<String
, JettyHttpContext
> contexts
= new TreeMap
<>();
54 protected final ContextHandlerCollection contextHandlerCollection
= new ContextHandlerCollection();
56 private boolean started
;
58 private CmsState cmsState
;
61 public void bind(InetSocketAddress addr
, int backlog
) throws IOException
{
62 throw new UnsupportedOperationException();
69 ThreadPool threadPool
= null;
70 if (executor
!= null) {
71 threadPool
= new ExecutorThreadPool(executor
);
73 // TODO make it configurable
74 threadPool
= new QueuedThreadPool(10, 1);
77 server
= new Server(threadPool
);
79 configureConnectors();
81 if (httpConnector
!= null) {
83 server
.addConnector(httpConnector
);
86 if (httpsConnector
!= null) {
87 httpsConnector
.open();
88 server
.addConnector(httpsConnector
);
94 ServletContextHandler rootContextHandler
= createRootContextHandler();
95 // httpContext.addServlet(holder, "/*");
96 if (rootContextHandler
!= null)
97 configureRootContextHandler(rootContextHandler
);
98 // server.setHandler(rootContextHandler);
100 // ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
101 if (rootContextHandler
!= null && !contexts
.containsKey("/"))
102 contextHandlerCollection
.addHandler(rootContextHandler
);
103 // for (String contextPath : contexts.keySet()) {
104 // JettyHttpContext ctx = contexts.get(contextPath);
105 // contextHandlers.addHandler(ctx.getContextHandler());
108 server
.setHandler(contextHandlerCollection
);
116 String httpHost
= getDeployProperty(CmsDeployProperty
.HOST
);
117 String fallBackHostname
= cmsState
!= null ? cmsState
.getHostname() : "::1";
118 if (httpConnector
!= null)
119 httpAddress
= new InetSocketAddress(httpHost
!= null ? httpHost
: fallBackHostname
,
120 httpConnector
.getLocalPort());
121 if (httpsConnector
!= null)
122 httpsAddress
= new InetSocketAddress(httpHost
!= null ? httpHost
: fallBackHostname
,
123 httpsConnector
.getLocalPort());
126 Runtime
.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
128 log
.info(httpPortsMsg());
130 } catch (Exception e
) {
131 throw new IllegalStateException("Cannot start Jetty HTTPS server", e
);
136 public void stop(int delay
) {
137 // TODO wait for processing to complete
144 // serverConnector.close();
146 // TODO delete temp dir
148 } catch (Exception e
) {
155 public void setExecutor(Executor executor
) {
156 if (!(executor
instanceof ThreadPoolExecutor
))
157 throw new IllegalArgumentException("Only " + ThreadPoolExecutor
.class.getName() + " are supported");
158 this.executor
= (ThreadPoolExecutor
) executor
;
162 public Executor
getExecutor() {
167 public synchronized HttpContext
createContext(String path
, HttpHandler handler
) {
168 HttpContext httpContext
= createContext(path
);
169 httpContext
.setHandler(handler
);
174 public synchronized HttpContext
createContext(String path
) {
175 if (contexts
.containsKey(path
))
176 throw new IllegalArgumentException("Context " + path
+ " already exists");
177 JettyHttpContext httpContext
= new JettyHttpContext(this, path
);
178 contexts
.put(path
, httpContext
);
180 contextHandlerCollection
.addHandler(httpContext
.getContextHandler());
185 public synchronized void removeContext(String path
) throws IllegalArgumentException
{
186 if (!contexts
.containsKey(path
))
187 throw new IllegalArgumentException("Context " + path
+ " does not exist");
188 JettyHttpContext httpContext
= contexts
.remove(path
);
189 // TODO stop handler first?
190 contextHandlerCollection
.removeHandler(httpContext
.getContextHandler());
194 public synchronized void removeContext(HttpContext context
) {
195 removeContext(context
.getPath());
199 public InetSocketAddress
getAddress() {
204 public void setHttpsConfigurator(HttpsConfigurator config
) {
205 this.httpsConfigurator
= config
;
209 public HttpsConfigurator
getHttpsConfigurator() {
210 return httpsConfigurator
;
213 protected void configureConnectors() {
214 HttpConfiguration httpConfiguration
= new HttpConfiguration();
216 String httpPortStr
= getDeployProperty(CmsDeployProperty
.HTTP_PORT
);
217 String httpsPortStr
= getDeployProperty(CmsDeployProperty
.HTTPS_PORT
);
219 /// TODO make it more generic
220 String httpHost
= getDeployProperty(CmsDeployProperty
.HOST
);
221 // String httpsHost = getFrameworkProp(
222 // JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
225 if (httpPortStr
!= null || httpsPortStr
!= null) {
226 // TODO deal with hostname resolving taking too much time
227 // String fallBackHostname = InetAddress.getLocalHost().getHostName();
229 boolean httpEnabled
= httpPortStr
!= null;
230 // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
231 boolean httpsEnabled
= httpsPortStr
!= null;
232 // props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
234 int httpsPort
= Integer
.parseInt(httpsPortStr
);
235 httpConfiguration
.setSecureScheme("https");
236 httpConfiguration
.setSecurePort(httpsPort
);
240 int httpPort
= Integer
.parseInt(httpPortStr
);
241 httpConnector
= new ServerConnector(server
, new HttpConnectionFactory(httpConfiguration
));
242 httpConnector
.setPort(httpPort
);
243 httpConnector
.setHost(httpHost
);
244 httpConnector
.setIdleTimeout(DEFAULT_IDLE_TIMEOUT
);
250 SslContextFactory
.Server sslContextFactory
= new SslContextFactory
.Server();
251 // sslContextFactory.setKeyStore(KeyS)
253 sslContextFactory
.setKeyStoreType(getDeployProperty(CmsDeployProperty
.SSL_KEYSTORETYPE
));
254 sslContextFactory
.setKeyStorePath(getDeployProperty(CmsDeployProperty
.SSL_KEYSTORE
));
255 sslContextFactory
.setKeyStorePassword(getDeployProperty(CmsDeployProperty
.SSL_PASSWORD
));
256 // sslContextFactory.setKeyManagerPassword(getFrameworkProp(CmsDeployProperty.SSL_KEYPASSWORD));
257 sslContextFactory
.setProtocol("TLS");
259 sslContextFactory
.setTrustStoreType(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORETYPE
));
260 sslContextFactory
.setTrustStorePath(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORE
));
261 sslContextFactory
.setTrustStorePassword(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTOREPASSWORD
));
263 String wantClientAuth
= getDeployProperty(CmsDeployProperty
.SSL_WANTCLIENTAUTH
);
264 if (wantClientAuth
!= null && wantClientAuth
.equals(Boolean
.toString(true)))
265 sslContextFactory
.setWantClientAuth(true);
266 String needClientAuth
= getDeployProperty(CmsDeployProperty
.SSL_NEEDCLIENTAUTH
);
267 if (needClientAuth
!= null && needClientAuth
.equals(Boolean
.toString(true)))
268 sslContextFactory
.setNeedClientAuth(true);
270 // HTTPS Configuration
271 HttpConfiguration https_config
= new HttpConfiguration(httpConfiguration
);
272 https_config
.addCustomizer(new SecureRequestCustomizer());
273 https_config
.setUriCompliance(UriCompliance
.LEGACY
);
276 httpsConnector
= new ServerConnector(server
, new SslConnectionFactory(sslContextFactory
, "http/1.1"),
277 new HttpConnectionFactory(https_config
));
278 int httpsPort
= Integer
.parseInt(httpsPortStr
);
279 httpsConnector
.setPort(httpsPort
);
280 httpsConnector
.setHost(httpHost
);
287 protected String
getDeployProperty(CmsDeployProperty deployProperty
) {
288 return cmsState
!= null ? cmsState
.getDeployProperty(deployProperty
.getProperty())
289 : System
.getProperty(deployProperty
.getProperty());
292 private String
httpPortsMsg() {
294 return (httpConnector
!= null ?
"HTTP " + getHttpPort() + " " : " ")
295 + (httpsConnector
!= null ?
"HTTPS " + getHttpsPort() : "");
298 public Integer
getHttpPort() {
299 if (httpConnector
== null)
301 return httpConnector
.getLocalPort();
304 public Integer
getHttpsPort() {
305 if (httpsConnector
== null)
307 return httpsConnector
.getLocalPort();
310 protected ServletContextHandler
createRootContextHandler() {
314 protected void configureRootContextHandler(ServletContextHandler servletContextHandler
) throws ServletException
{
318 public void setCmsState(CmsState cmsState
) {
319 this.cmsState
= cmsState
;
322 public boolean isStarted() {
326 public static void main(String
... args
) {
327 JettyHttpServer httpServer
= new JettyHttpServer();
328 System
.setProperty("argeo.http.port", "8080");
329 httpServer
.createContext("/", (exchange
) -> {
330 exchange
.getResponseBody().write("Hello World!".getBytes());
333 httpServer
.createContext("/sub/context", (exchange
) -> {
334 final String key
= "count";
335 Integer count
= (Integer
) exchange
.getHttpContext().getAttributes().get(key
);
337 exchange
.getHttpContext().getAttributes().put(key
, 0);
339 exchange
.getHttpContext().getAttributes().put(key
, count
+ 1);
340 StringBuilder sb
= new StringBuilder();
341 sb
.append("Subcontext:");
342 sb
.append(" " + key
+ "=" + exchange
.getHttpContext().getAttributes().get(key
));
343 sb
.append(" relativePath=" + HttpServerUtils
.relativize(exchange
));
344 exchange
.getResponseBody().write(sb
.toString().getBytes());