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
.cms
.CmsDeployProperty
;
14 import org
.argeo
.util
.http
.HttpServerUtils
;
15 import org
.eclipse
.jetty
.http
.UriCompliance
;
16 import org
.eclipse
.jetty
.server
.HttpConfiguration
;
17 import org
.eclipse
.jetty
.server
.HttpConnectionFactory
;
18 import org
.eclipse
.jetty
.server
.SecureRequestCustomizer
;
19 import org
.eclipse
.jetty
.server
.Server
;
20 import org
.eclipse
.jetty
.server
.ServerConnector
;
21 import org
.eclipse
.jetty
.server
.SslConnectionFactory
;
22 import org
.eclipse
.jetty
.server
.handler
.ContextHandlerCollection
;
23 import org
.eclipse
.jetty
.servlet
.ServletContextHandler
;
24 import org
.eclipse
.jetty
.util
.ssl
.SslContextFactory
;
25 import org
.eclipse
.jetty
.util
.thread
.ExecutorThreadPool
;
26 import org
.eclipse
.jetty
.util
.thread
.QueuedThreadPool
;
27 import org
.eclipse
.jetty
.util
.thread
.ThreadPool
;
29 import com
.sun
.net
.httpserver
.HttpContext
;
30 import com
.sun
.net
.httpserver
.HttpHandler
;
31 import com
.sun
.net
.httpserver
.HttpsConfigurator
;
32 import com
.sun
.net
.httpserver
.HttpsServer
;
34 public class JettyHttpServer
extends HttpsServer
{
35 private final static CmsLog log
= CmsLog
.getLog(JettyHttpServer
.class);
37 private static final int DEFAULT_IDLE_TIMEOUT
= 30000;
39 private Server server
;
41 protected ServerConnector httpConnector
;
42 protected ServerConnector httpsConnector
;
44 private InetSocketAddress address
;
46 private ThreadPoolExecutor executor
;
48 private HttpsConfigurator httpsConfigurator
;
50 private final Map
<String
, JettyHttpContext
> contexts
= new TreeMap
<>();
52 protected final ContextHandlerCollection contextHandlerCollection
= new ContextHandlerCollection();
54 private boolean started
;
57 public void bind(InetSocketAddress addr
, int backlog
) throws IOException
{
58 throw new UnsupportedOperationException();
65 ThreadPool threadPool
= null;
66 if (executor
!= null) {
67 threadPool
= new ExecutorThreadPool(executor
);
69 // TODO make it configurable
70 threadPool
= new QueuedThreadPool(10, 1);
73 server
= new Server(threadPool
);
75 configureConnectors();
77 if (httpConnector
!= null) {
79 server
.addConnector(httpConnector
);
82 if (httpsConnector
!= null) {
83 httpsConnector
.open();
84 server
.addConnector(httpsConnector
);
90 ServletContextHandler rootContextHandler
= createRootContextHandler();
91 // httpContext.addServlet(holder, "/*");
92 if (rootContextHandler
!= null)
93 configureRootContextHandler(rootContextHandler
);
94 // server.setHandler(rootContextHandler);
96 // ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
97 if (rootContextHandler
!= null && !contexts
.containsKey("/"))
98 contextHandlerCollection
.addHandler(rootContextHandler
);
99 // for (String contextPath : contexts.keySet()) {
100 // JettyHttpContext ctx = contexts.get(contextPath);
101 // contextHandlers.addHandler(ctx.getContextHandler());
104 server
.setHandler(contextHandlerCollection
);
111 Runtime
.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
113 log
.info(httpPortsMsg());
115 } catch (Exception e
) {
116 throw new IllegalStateException("Cannot start Jetty HTTPS server", e
);
121 public void stop(int delay
) {
122 // TODO wait for processing to complete
129 // serverConnector.close();
131 // TODO delete temp dir
133 } catch (Exception e
) {
140 public void setExecutor(Executor executor
) {
141 if (!(executor
instanceof ThreadPoolExecutor
))
142 throw new IllegalArgumentException("Only " + ThreadPoolExecutor
.class.getName() + " are supported");
143 this.executor
= (ThreadPoolExecutor
) executor
;
147 public Executor
getExecutor() {
152 public synchronized HttpContext
createContext(String path
, HttpHandler handler
) {
153 HttpContext httpContext
= createContext(path
);
154 httpContext
.setHandler(handler
);
159 public synchronized HttpContext
createContext(String path
) {
160 if (contexts
.containsKey(path
))
161 throw new IllegalArgumentException("Context " + path
+ " already exists");
162 JettyHttpContext httpContext
= new JettyHttpContext(this, path
);
163 contexts
.put(path
, httpContext
);
165 contextHandlerCollection
.addHandler(httpContext
.getContextHandler());
170 public synchronized void removeContext(String path
) throws IllegalArgumentException
{
171 if (!contexts
.containsKey(path
))
172 throw new IllegalArgumentException("Context " + path
+ " does not exist");
173 JettyHttpContext httpContext
= contexts
.remove(path
);
174 // TODO stop handler first?
175 contextHandlerCollection
.removeHandler(httpContext
.getContextHandler());
179 public synchronized void removeContext(HttpContext context
) {
180 removeContext(context
.getPath());
184 public InetSocketAddress
getAddress() {
189 public void setHttpsConfigurator(HttpsConfigurator config
) {
190 this.httpsConfigurator
= config
;
194 public HttpsConfigurator
getHttpsConfigurator() {
195 return httpsConfigurator
;
200 protected void configureConnectors() {
201 HttpConfiguration httpConfiguration
= new HttpConfiguration();
203 String httpPortStr
= getDeployProperty(CmsDeployProperty
.HTTP_PORT
);
204 String httpsPortStr
= getDeployProperty(CmsDeployProperty
.HTTPS_PORT
);
206 /// TODO make it more generic
207 String httpHost
= getDeployProperty(CmsDeployProperty
.HOST
);
208 // String httpsHost = getFrameworkProp(
209 // JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
212 if (httpPortStr
!= null || httpsPortStr
!= null) {
213 boolean httpEnabled
= httpPortStr
!= null;
214 // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
215 boolean httpsEnabled
= httpsPortStr
!= null;
216 // props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
218 int httpsPort
= Integer
.parseInt(httpsPortStr
);
219 httpConfiguration
.setSecureScheme("https");
220 httpConfiguration
.setSecurePort(httpsPort
);
224 int httpPort
= Integer
.parseInt(httpPortStr
);
225 httpConnector
= new ServerConnector(server
, new HttpConnectionFactory(httpConfiguration
));
226 httpConnector
.setPort(httpPort
);
227 httpConnector
.setHost(httpHost
);
228 httpConnector
.setIdleTimeout(DEFAULT_IDLE_TIMEOUT
);
233 SslContextFactory
.Server sslContextFactory
= new SslContextFactory
.Server();
234 // sslContextFactory.setKeyStore(KeyS)
236 sslContextFactory
.setKeyStoreType(getDeployProperty(CmsDeployProperty
.SSL_KEYSTORETYPE
));
237 sslContextFactory
.setKeyStorePath(getDeployProperty(CmsDeployProperty
.SSL_KEYSTORE
));
238 sslContextFactory
.setKeyStorePassword(getDeployProperty(CmsDeployProperty
.SSL_PASSWORD
));
239 // sslContextFactory.setKeyManagerPassword(getFrameworkProp(CmsDeployProperty.SSL_KEYPASSWORD));
240 sslContextFactory
.setProtocol("TLS");
242 sslContextFactory
.setTrustStoreType(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORETYPE
));
243 sslContextFactory
.setTrustStorePath(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORE
));
244 sslContextFactory
.setTrustStorePassword(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTOREPASSWORD
));
246 String wantClientAuth
= getDeployProperty(CmsDeployProperty
.SSL_WANTCLIENTAUTH
);
247 if (wantClientAuth
!= null && wantClientAuth
.equals(Boolean
.toString(true)))
248 sslContextFactory
.setWantClientAuth(true);
249 String needClientAuth
= getDeployProperty(CmsDeployProperty
.SSL_NEEDCLIENTAUTH
);
250 if (needClientAuth
!= null && needClientAuth
.equals(Boolean
.toString(true)))
251 sslContextFactory
.setNeedClientAuth(true);
253 // HTTPS Configuration
254 HttpConfiguration https_config
= new HttpConfiguration(httpConfiguration
);
255 https_config
.addCustomizer(new SecureRequestCustomizer());
256 https_config
.setUriCompliance(UriCompliance
.LEGACY
);
259 httpsConnector
= new ServerConnector(server
, new SslConnectionFactory(sslContextFactory
, "http/1.1"),
260 new HttpConnectionFactory(https_config
));
261 int httpsPort
= Integer
.parseInt(httpsPortStr
);
262 httpsConnector
.setPort(httpsPort
);
263 httpsConnector
.setHost(httpHost
);
270 protected String
getDeployProperty(CmsDeployProperty deployProperty
) {
271 return System
.getProperty(deployProperty
.getProperty());
274 private String
httpPortsMsg() {
276 return (httpConnector
!= null ?
"HTTP " + getHttpPort() + " " : " ")
277 + (httpsConnector
!= null ?
"HTTPS " + getHttpsPort() : "");
280 public Integer
getHttpPort() {
281 if (httpConnector
== null)
283 return httpConnector
.getLocalPort();
286 public Integer
getHttpsPort() {
287 if (httpsConnector
== null)
289 return httpsConnector
.getLocalPort();
292 protected ServletContextHandler
createRootContextHandler() {
296 protected void configureRootContextHandler(ServletContextHandler servletContextHandler
) throws ServletException
{
301 public boolean isStarted() {
305 public static void main(String
... args
) {
306 JettyHttpServer httpServer
= new JettyHttpServer();
307 System
.setProperty("argeo.http.port", "8080");
308 httpServer
.createContext("/", (exchange
) -> {
309 exchange
.getResponseBody().write("Hello World!".getBytes());
312 httpServer
.createContext("/sub/context", (exchange
) -> {
313 final String key
= "count";
314 Integer count
= (Integer
) exchange
.getHttpContext().getAttributes().get(key
);
316 exchange
.getHttpContext().getAttributes().put(key
, 0);
318 exchange
.getHttpContext().getAttributes().put(key
, count
+ 1);
319 StringBuilder sb
= new StringBuilder();
320 sb
.append("Subcontext:");
321 sb
.append(" " + key
+ "=" + exchange
.getHttpContext().getAttributes().get(key
));
322 sb
.append(" relativePath=" + HttpServerUtils
.relativize(exchange
));
323 exchange
.getResponseBody().write(sb
.toString().getBytes());