1 package org
.argeo
.cms
.internal
.runtime
;
3 import static org
.argeo
.api
.cms
.CmsConstants
.CONTEXT_PATH
;
6 import java
.util
.Objects
;
7 import java
.util
.TreeMap
;
8 import java
.util
.concurrent
.CompletableFuture
;
9 import java
.util
.concurrent
.CompletionStage
;
11 import org
.argeo
.api
.cms
.CmsConstants
;
12 import org
.argeo
.api
.cms
.CmsDeployment
;
13 import org
.argeo
.api
.cms
.CmsLog
;
14 import org
.argeo
.api
.cms
.CmsSshd
;
15 import org
.argeo
.api
.cms
.CmsState
;
16 import org
.argeo
.cms
.CmsDeployProperty
;
17 import org
.argeo
.cms
.internal
.http
.CmsAuthenticator
;
18 import org
.argeo
.cms
.internal
.http
.PublicCmsAuthenticator
;
20 import com
.sun
.net
.httpserver
.HttpContext
;
21 import com
.sun
.net
.httpserver
.HttpHandler
;
22 import com
.sun
.net
.httpserver
.HttpServer
;
24 /** Reference implementation of {@link CmsDeployment}. */
25 public class CmsDeploymentImpl
implements CmsDeployment
{
26 private final CmsLog log
= CmsLog
.getLog(getClass());
28 private CmsState cmsState
;
31 private boolean httpExpected
= false;
32 private boolean sshdExpected
= false;
35 private CompletableFuture
<HttpServer
> httpServer
= new CompletableFuture
<>();
36 private Map
<String
, HttpHandler
> httpHandlers
= new TreeMap
<>();
37 private Map
<String
, CmsAuthenticator
> httpAuthenticators
= new TreeMap
<>();
40 private CompletableFuture
<CmsSshd
> cmsSshd
= new CompletableFuture
<>();
43 log
.debug(() -> "CMS deployment available");
49 public void setCmsState(CmsState cmsState
) {
50 this.cmsState
= cmsState
;
52 String httpPort
= this.cmsState
.getDeployProperty(CmsDeployProperty
.HTTP_PORT
.getProperty());
53 String httpsPort
= this.cmsState
.getDeployProperty(CmsDeployProperty
.HTTPS_PORT
.getProperty());
54 httpExpected
= httpPort
!= null || httpsPort
!= null;
56 httpServer
.complete(null);
58 String sshdPort
= this.cmsState
.getDeployProperty(CmsDeployProperty
.SSHD_PORT
.getProperty());
59 sshdExpected
= sshdPort
!= null;
61 cmsSshd
.complete(null);
64 public void setHttpServer(HttpServer httpServer
) {
65 Objects
.requireNonNull(httpServer
);
66 if (this.httpServer
.isDone())
68 throw new IllegalStateException("HTTP server is already set");
71 // create contexts whose handlers had already been published
72 synchronized (httpHandlers
) {
73 synchronized (httpAuthenticators
) {
74 this.httpServer
.complete(httpServer
);
75 for (String contextPath
: httpHandlers
.keySet()) {
76 HttpHandler httpHandler
= httpHandlers
.get(contextPath
);
77 CmsAuthenticator authenticator
= httpAuthenticators
.get(contextPath
);
78 createHttpContext(contextPath
, httpHandler
, authenticator
);
84 public void addHttpHandler(HttpHandler httpHandler
, Map
<String
, String
> properties
) {
85 final String contextPath
= properties
.get(CONTEXT_PATH
);
86 if (contextPath
== null) {
87 log
.warn("Property " + CONTEXT_PATH
+ " not set on HTTP handler " + properties
+ ". Ignoring it.");
90 boolean isPublic
= Boolean
.parseBoolean(properties
.get(CmsConstants
.CONTEXT_PUBLIC
));
91 CmsAuthenticator authenticator
= isPublic ?
new PublicCmsAuthenticator() : new CmsAuthenticator();
92 synchronized (httpHandlers
) {
93 synchronized (httpAuthenticators
) {
94 httpHandlers
.put(contextPath
, httpHandler
);
95 httpAuthenticators
.put(contextPath
, authenticator
);
98 if (!httpServer
.isDone()) {
101 createHttpContext(contextPath
, httpHandler
, authenticator
);
105 public void createHttpContext(String contextPath
, HttpHandler httpHandler
, CmsAuthenticator authenticator
) {
107 if (log
.isTraceEnabled())
108 log
.warn("Ignore HTTP context " + contextPath
+ " as we don't provide an HTTP server");
111 if (!this.httpServer
.isDone())
112 throw new IllegalStateException("HTTP server is not set");
113 // TODO use resultNow when switching to Java 21
114 HttpContext httpContext
= httpServer
.join().createContext(contextPath
);
115 // we want to set the authenticator BEFORE the handler actually becomes active
116 httpContext
.setAuthenticator(authenticator
);
117 httpContext
.setHandler(httpHandler
);
118 log
.debug(() -> "Added handler " + contextPath
+ " : " + httpHandler
.getClass().getName());
121 public void removeHttpHandler(HttpHandler httpHandler
, Map
<String
, String
> properties
) {
122 final String contextPath
= properties
.get(CmsConstants
.CONTEXT_PATH
);
123 if (contextPath
== null)
124 return; // ignore silently
125 httpHandlers
.remove(contextPath
);
126 if (!httpExpected
|| !httpServer
.isDone())
128 // TODO use resultNow when switching to Java 21
129 httpServer
.join().removeContext(contextPath
);
130 log
.debug(() -> "Removed handler " + contextPath
+ " : " + httpHandler
.getClass().getName());
133 public boolean allExpectedServicesAvailable() {
134 if (httpExpected
&& !httpServer
.isDone())
136 if (sshdExpected
&& !cmsSshd
.isDone())
141 public void setCmsSshd(CmsSshd cmsSshd
) {
142 Objects
.requireNonNull(cmsSshd
);
143 this.cmsSshd
.complete(cmsSshd
);
147 public CompletionStage
<HttpServer
> getHttpServer() {
148 return httpServer
.minimalCompletionStage();
152 public CompletionStage
<CmsSshd
> getCmsSshd() {
153 return cmsSshd
.minimalCompletionStage();