1 package org
.argeo
.cms
.internal
.runtime
;
3 import java
.lang
.management
.ManagementFactory
;
4 import java
.util
.ArrayList
;
5 import java
.util
.HashMap
;
6 import java
.util
.IdentityHashMap
;
8 import java
.util
.Locale
;
10 import java
.util
.TreeMap
;
11 import java
.util
.UUID
;
12 import java
.util
.concurrent
.CompletableFuture
;
13 import java
.util
.concurrent
.ExecutionException
;
14 import java
.util
.concurrent
.Flow
;
15 import java
.util
.concurrent
.Flow
.Subscription
;
16 import java
.util
.concurrent
.SubmissionPublisher
;
18 import javax
.security
.auth
.Subject
;
20 import org
.argeo
.api
.cms
.CmsContext
;
21 import org
.argeo
.api
.cms
.CmsDeployment
;
22 import org
.argeo
.api
.cms
.CmsEventSubscriber
;
23 import org
.argeo
.api
.cms
.CmsLog
;
24 import org
.argeo
.api
.cms
.CmsSession
;
25 import org
.argeo
.api
.cms
.CmsSessionId
;
26 import org
.argeo
.api
.cms
.CmsState
;
27 import org
.argeo
.api
.uuid
.UuidFactory
;
28 import org
.argeo
.cms
.CmsDeployProperty
;
29 import org
.argeo
.cms
.internal
.auth
.CmsSessionImpl
;
30 import org
.ietf
.jgss
.GSSCredential
;
31 import org
.osgi
.service
.useradmin
.UserAdmin
;
33 public class CmsContextImpl
implements CmsContext
{
35 private final CmsLog log
= CmsLog
.getLog(getClass());
36 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
38 // private EgoRepository egoRepository;
39 private static CompletableFuture
<CmsContextImpl
> instance
= new CompletableFuture
<CmsContextImpl
>();
41 private CmsState cmsState
;
42 private CmsDeployment cmsDeployment
;
43 private UserAdmin userAdmin
;
44 private UuidFactory uuidFactory
;
45 // private ProvidedRepository contentRepository;
48 private Locale defaultLocale
;
49 private List
<Locale
> locales
= null;
51 private Long availableSince
;
54 private Map
<UUID
, CmsSessionImpl
> cmsSessionsByUuid
= new HashMap
<>();
55 private Map
<String
, CmsSessionImpl
> cmsSessionsByLocalId
= new HashMap
<>();
58 private Map
<String
, SubmissionPublisher
<Map
<String
, Object
>>> topics
= new TreeMap
<>();
59 // private IdentityHashMap<CmsEventSubscriber, List<CmsEventFlowSubscriber>> subscriptions = new IdentityHashMap<>();
61 // public CmsContextImpl() {
66 List
<String
> codes
= CmsStateImpl
.getDeployProperties(cmsState
, CmsDeployProperty
.LOCALE
);
67 locales
= getLocaleList(codes
);
68 if (locales
.size() == 0)
69 throw new IllegalStateException("At least one locale must be set");
70 defaultLocale
= locales
.get(0);
71 // Object defaultLocaleValue = KernelUtils.getFrameworkProp(CmsConstants.I18N_DEFAULT_LOCALE);
72 // defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
73 // : new Locale(ENGLISH.getLanguage());
75 // new ServiceTracker<Repository, Repository>(bc, Repository.class, null) {
77 // public Repository addingService(ServiceReference<Repository> reference) {
78 // Object cn = reference.getProperty(NodeConstants.CN);
79 // if (cn != null && cn.equals(NodeConstants.EGO_REPOSITORY)) {
80 //// egoRepository = (EgoRepository) bc.getService(reference);
81 // if (log.isTraceEnabled())
82 // log.trace("Home repository is available");
84 // return super.addingService(reference);
88 // public void removedService(ServiceReference<Repository> reference, Repository service) {
89 // super.removedService(reference, service);
90 //// egoRepository = null;
105 * Checks whether the deployment is available according to expectations, and
106 * mark it as available.
108 private void checkReadiness() {
111 if (cmsDeployment
!= null && userAdmin
!= null) {
112 String data
= KernelUtils
.getFrameworkProp(KernelUtils
.OSGI_INSTANCE_AREA
);
113 String state
= KernelUtils
.getFrameworkProp(KernelUtils
.OSGI_CONFIGURATION_AREA
);
114 availableSince
= System
.currentTimeMillis();
115 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
116 String jvmUptimeStr
= " in " + (jvmUptime
/ 1000) + "." + (jvmUptime
% 1000) + "s";
117 log
.info("## ARGEO CMS AVAILABLE" + (log
.isDebugEnabled() ? jvmUptimeStr
: "") + " ##");
118 if (log
.isDebugEnabled()) {
119 log
.debug("## state: " + state
);
121 log
.debug("## data: " + data
);
123 long begin
= cmsState
.getAvailableSince();
124 long initDuration
= System
.currentTimeMillis() - begin
;
125 if (log
.isTraceEnabled())
126 log
.trace("Kernel initialization took " + initDuration
+ "ms");
127 tributeToFreeSoftware(initDuration
);
129 throw new IllegalStateException("Deployment is not available");
133 final private void tributeToFreeSoftware(long initDuration
) {
134 if (log
.isTraceEnabled()) {
135 long ms
= initDuration
/ 100;
136 log
.trace("Spend " + ms
+ "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
137 long beginNano
= System
.nanoTime();
140 } catch (InterruptedException e
) {
143 long durationNano
= System
.nanoTime() - beginNano
;
144 final double M
= 1000d
* 1000d
;
145 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
146 log
.trace("Sleep accuracy: " + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100)) + " %");
151 public void createWorkgroup(String dn
) {
152 // if (egoRepository == null)
153 // throw new CmsException("Ego repository is not available");
154 // // TODO add check that the group exists
155 // egoRepository.createWorkgroup(dn);
156 throw new UnsupportedOperationException();
159 /** Returns null if argument is null. */
160 private static List
<Locale
> getLocaleList(List
<String
> codes
) {
163 ArrayList
<Locale
> availableLocales
= new ArrayList
<Locale
>();
164 for (String code
: codes
) {
167 // variant not supported
168 int indexUnd
= code
.indexOf("_");
171 String language
= code
.substring(0, indexUnd
);
172 String country
= code
.substring(indexUnd
+ 1);
173 locale
= new Locale(language
, country
);
175 locale
= new Locale(code
);
177 availableLocales
.add(locale
);
179 return availableLocales
;
182 public void setCmsDeployment(CmsDeployment cmsDeployment
) {
183 this.cmsDeployment
= cmsDeployment
;
186 public void setCmsState(CmsState cmsState
) {
187 this.cmsState
= cmsState
;
190 public void setUserAdmin(UserAdmin userAdmin
) {
191 this.userAdmin
= userAdmin
;
194 public UuidFactory
getUuidFactory() {
198 public void setUuidFactory(UuidFactory uuidFactory
) {
199 this.uuidFactory
= uuidFactory
;
202 // public ProvidedRepository getContentRepository() {
203 // return contentRepository;
206 // public void setContentRepository(ProvidedRepository contentRepository) {
207 // this.contentRepository = contentRepository;
211 public Locale
getDefaultLocale() {
212 return defaultLocale
;
216 public List
<Locale
> getLocales() {
221 public synchronized Long
getAvailableSince() {
222 return availableSince
;
225 public synchronized boolean isAvailable() {
226 return availableSince
!= null;
230 public CmsState
getCmsState() {
238 public synchronized static CmsContextImpl
getCmsContext() {
239 return getInstance();
242 // /** Required by USER login module. */
243 // public synchronized static UserAdmin getUserAdmin() {
244 // return getInstance().userAdmin;
247 /** Required by SPNEGO login module. */
249 public synchronized static GSSCredential
getAcceptorCredentials() {
250 // FIXME find a cleaner way
251 return ((CmsUserAdmin
) getInstance().userAdmin
).getAcceptorCredentials();
254 private synchronized static void setInstance(CmsContextImpl cmsContextImpl
) {
255 if (cmsContextImpl
!= null) {
256 if (instance
.isDone())
257 throw new IllegalStateException("CMS Context is already set");
258 instance
.complete(cmsContextImpl
);
260 instance
= new CompletableFuture
<CmsContextImpl
>();
264 private synchronized static CmsContextImpl
getInstance() {
266 return instance
.get();
267 } catch (InterruptedException
| ExecutionException e
) {
268 throw new IllegalStateException("Cannot retrieve CMS Context", e
);
272 public UserAdmin
getUserAdmin() {
281 public synchronized CmsSession
getCmsSession(Subject subject
) {
282 if (subject
.getPrivateCredentials(CmsSessionId
.class).isEmpty())
284 CmsSessionId cmsSessionId
= subject
.getPrivateCredentials(CmsSessionId
.class).iterator().next();
285 return getCmsSessionByUuid(cmsSessionId
.getUuid());
288 public synchronized void registerCmsSession(CmsSessionImpl cmsSession
) {
289 if (cmsSessionsByUuid
.containsKey(cmsSession
.getUuid())
290 || cmsSessionsByLocalId
.containsKey(cmsSession
.getLocalId()))
291 throw new IllegalStateException("CMS session " + cmsSession
+ " is already registered.");
292 cmsSessionsByUuid
.put(cmsSession
.getUuid(), cmsSession
);
293 cmsSessionsByLocalId
.put(cmsSession
.getLocalId(), cmsSession
);
296 public synchronized void unregisterCmsSession(CmsSessionImpl cmsSession
) {
297 if (!cmsSessionsByUuid
.containsKey(cmsSession
.getUuid())
298 || !cmsSessionsByLocalId
.containsKey(cmsSession
.getLocalId()))
299 throw new IllegalStateException("CMS session " + cmsSession
+ " is not registered.");
300 CmsSession removed
= cmsSessionsByUuid
.remove(cmsSession
.getUuid());
301 assert removed
== cmsSession
;
302 cmsSessionsByLocalId
.remove(cmsSession
.getLocalId());
306 * The {@link CmsSession} related to this UUID, or <code>null</null> if not
309 public synchronized CmsSessionImpl
getCmsSessionByUuid(UUID uuid
) {
310 return cmsSessionsByUuid
.get(uuid
);
314 * The {@link CmsSession} related to this local id, or <code>null</null> if not
317 public synchronized CmsSessionImpl
getCmsSessionByLocalId(String localId
) {
318 return cmsSessionsByLocalId
.get(localId
);
324 public void sendEvent(String topic
, Map
<String
, Object
> event
) {
325 SubmissionPublisher
<Map
<String
, Object
>> publisher
= topics
.get(topic
);
326 if (publisher
== null)
327 return; // no one is interested
328 publisher
.submit(event
);
331 public void addEventSubscriber(String topic
, CmsEventSubscriber subscriber
) {
332 synchronized (topics
) {
333 if (!topics
.containsKey(topic
))
334 topics
.put(topic
, new SubmissionPublisher
<>());
336 SubmissionPublisher
<Map
<String
, Object
>> publisher
= topics
.get(topic
);
337 CmsEventFlowSubscriber flowSubscriber
= new CmsEventFlowSubscriber(topic
, subscriber
);
338 publisher
.subscribe(flowSubscriber
);
341 public void removeEventSubscriber(String topic
, CmsEventSubscriber subscriber
) {
342 SubmissionPublisher
<Map
<String
, Object
>> publisher
= topics
.get(topic
);
343 if (publisher
== null) {
344 log
.error("There should be an event topic " + topic
);
347 for (Flow
.Subscriber
<?
super Map
<String
, Object
>> flowSubscriber
: publisher
.getSubscribers()) {
348 if (flowSubscriber
instanceof CmsEventFlowSubscriber
)
349 ((CmsEventFlowSubscriber
) flowSubscriber
).unsubscribe();
351 synchronized (topics
) {
352 if (!publisher
.hasSubscribers()) {
354 topics
.remove(topic
);
359 static class CmsEventFlowSubscriber
implements Flow
.Subscriber
<Map
<String
, Object
>> {
360 private String topic
;
361 private CmsEventSubscriber eventSubscriber
;
363 private Subscription subscription
;
365 public CmsEventFlowSubscriber(String topic
, CmsEventSubscriber eventSubscriber
) {
367 this.eventSubscriber
= eventSubscriber
;
371 public void onSubscribe(Subscription subscription
) {
372 this.subscription
= subscription
;
373 subscription
.request(Long
.MAX_VALUE
);
377 public void onNext(Map
<String
, Object
> item
) {
378 eventSubscriber
.onEvent(topic
, item
);
383 public void onError(Throwable throwable
) {
384 // TODO Auto-generated method stub
389 public void onComplete() {
390 // TODO Auto-generated method stub
395 if (subscription
!= null)
396 subscription
.cancel();
398 throw new IllegalStateException("No subscription to cancel");