1 package org
.argeo
.cms
.ui
.rcp
.servlet
;
3 import java
.io
.IOException
;
4 import java
.lang
.System
.Logger
;
5 import java
.lang
.System
.Logger
.Level
;
6 import java
.net
.DatagramSocket
;
7 import java
.net
.ServerSocket
;
9 import java
.nio
.charset
.StandardCharsets
;
10 import java
.nio
.file
.Files
;
11 import java
.nio
.file
.Path
;
12 import java
.util
.Collections
;
13 import java
.util
.HashMap
;
14 import java
.util
.Hashtable
;
16 import java
.util
.concurrent
.CompletableFuture
;
18 import javax
.servlet
.Servlet
;
20 import org
.argeo
.api
.cms
.CmsApp
;
21 import org
.argeo
.cms
.ui
.rcp
.CmsRcpDisplayFactory
;
22 import org
.osgi
.framework
.BundleContext
;
23 import org
.osgi
.framework
.FrameworkUtil
;
24 import org
.osgi
.framework
.ServiceRegistration
;
25 import org
.osgi
.service
.event
.EventAdmin
;
26 import org
.osgi
.service
.http
.HttpService
;
28 /** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */
29 public class CmsRcpServletFactory
{
30 private final static Logger logger
= System
.getLogger(CmsRcpServletFactory
.class.getName());
32 private BundleContext bundleContext
= FrameworkUtil
.getBundle(CmsRcpServletFactory
.class).getBundleContext();
34 private CompletableFuture
<EventAdmin
> eventAdmin
= new CompletableFuture
<>();
36 private Map
<String
, ServiceRegistration
<Servlet
>> registrations
= Collections
.synchronizedMap(new HashMap
<>());
42 public void destroy() {
43 Path runFile
= CmsRcpDisplayFactory
.getUrlRunFile();
45 if (Files
.exists(runFile
)) {
46 Files
.delete(runFile
);
48 } catch (IOException e
) {
49 logger
.log(Level
.ERROR
, "Cannot delete " + runFile
, e
);
53 public void addCmsApp(CmsApp cmsApp
, Map
<String
, String
> properties
) {
54 String contextName
= properties
.get(CmsApp
.CONTEXT_NAME_PROPERTY
);
55 if (contextName
!= null) {
56 eventAdmin
.thenAccept((eventAdmin
) -> {
57 CmsRcpServlet servlet
= new CmsRcpServlet(eventAdmin
, cmsApp
);
58 Hashtable
<String
, String
> serviceProperties
= new Hashtable
<>();
59 serviceProperties
.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName
+ "/*");
60 ServiceRegistration
<Servlet
> sr
= bundleContext
.registerService(Servlet
.class, servlet
,
62 registrations
.put(contextName
, sr
);
67 public void removeCmsApp(CmsApp cmsApp
, Map
<String
, String
> properties
) {
68 String contextName
= properties
.get(CmsApp
.CONTEXT_NAME_PROPERTY
);
69 if (contextName
!= null) {
70 ServiceRegistration
<Servlet
> sr
= registrations
.get(contextName
);
75 public void setEventAdmin(EventAdmin eventAdmin
) {
76 this.eventAdmin
.complete(eventAdmin
);
79 public void setHttpService(HttpService httpService
, Map
<String
, Object
> properties
) {
80 Integer httpPort
= Integer
.parseInt(properties
.get("http.port").toString());
81 String baseUrl
= "http://localhost:" + httpPort
+ "/";
82 Path runFile
= CmsRcpDisplayFactory
.getUrlRunFile();
84 if (!Files
.exists(runFile
)) {
85 Files
.createDirectories(runFile
.getParent());
86 // TODO give read permission only to the owner
87 Files
.createFile(runFile
);
89 URI uri
= URI
.create(Files
.readString(runFile
));
90 if (!httpPort
.equals(uri
.getPort()))
91 if (!isPortAvailable(uri
.getPort())) {
92 throw new IllegalStateException("Another CMS is running on " + runFile
);
94 logger
.log(Level
.WARNING
,
95 "Run file " + runFile
+ " found but port of " + uri
+ " is available. Overwriting...");
98 Files
.writeString(runFile
, baseUrl
, StandardCharsets
.UTF_8
);
99 } catch (IOException e
) {
100 throw new RuntimeException("Cannot write run file to " + runFile
, e
);
102 logger
.log(Level
.DEBUG
, "RCP available under " + baseUrl
+ ", written to " + runFile
);
105 protected boolean isPortAvailable(int port
) {
106 ServerSocket ss
= null;
107 DatagramSocket ds
= null;
109 ss
= new ServerSocket(port
);
110 ss
.setReuseAddress(true);
111 ds
= new DatagramSocket(port
);
112 ds
.setReuseAddress(true);
114 } catch (IOException e
) {
123 } catch (IOException e
) {
124 /* should not be thrown */