import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Flow;
-import java.util.concurrent.Flow.Subscription;
-import java.util.concurrent.SubmissionPublisher;
import javax.security.auth.Subject;
import org.argeo.api.cms.CmsContext;
import org.argeo.api.cms.CmsDeployment;
-import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsEventBus;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.CmsSessionId;
import org.ietf.jgss.GSSCredential;
import org.osgi.service.useradmin.UserAdmin;
+/** Reference implementation of {@link CmsContext}. */
public class CmsContextImpl implements CmsContext {
private final CmsLog log = CmsLog.getLog(getClass());
private static CompletableFuture<CmsContextImpl> instance = new CompletableFuture<CmsContextImpl>();
-// private static CmsContextImpl instance = null;
private CmsState cmsState;
private CmsDeployment cmsDeployment;
private UserAdmin userAdmin;
private UuidFactory uuidFactory;
-// private ProvidedRepository contentRepository;
+ private CmsEventBus cmsEventBus;
// i18n
private Locale defaultLocale;
private Long availableSince;
+ // time in ms to wait for CMS to be ready
+ private final long readynessTimeout = 30 * 1000;
+
// CMS sessions
private Map<UUID, CmsSessionImpl> cmsSessionsByUuid = new HashMap<>();
private Map<String, CmsSessionImpl> cmsSessionsByLocalId = new HashMap<>();
- // CMS events
- private Map<String, SubmissionPublisher<Map<String, Object>>> topics = new TreeMap<>();
-// private IdentityHashMap<CmsEventSubscriber, List<CmsEventFlowSubscriber>> subscriptions = new IdentityHashMap<>();
-
public void start() {
List<String> codes = CmsStateImpl.getDeployProperties(cmsState, CmsDeployProperty.LOCALE);
locales = getLocaleList(codes);
defaultLocale = locales.get(0);
new Thread(() -> {
- while (!checkReadiness()) {
+ long begin = System.currentTimeMillis();
+ long duration = 0;
+ readyness: while (!checkReadiness()) {
+ duration = System.currentTimeMillis() - begin;
+ if (duration > readynessTimeout) {
+ log.error("## CMS not ready after " + duration + " ms. Giving up checking.");
+ break readyness;
+ }
try {
- Thread.sleep(500);
+ Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}, "Check readiness").start();
-
- // checkReadiness();
-
setInstance(this);
}
availableSince = System.currentTimeMillis();
long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
String jvmUptimeStr = " in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
- log.info("## ARGEO CMS AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "") + " ##");
+ log.info("## ARGEO CMS " + cmsState.getUuid() + " AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "")
+ + " ##");
if (log.isDebugEnabled()) {
log.debug("## state: " + state);
if (data != null)
this.uuidFactory = uuidFactory;
}
-// public ProvidedRepository getContentRepository() {
-// return contentRepository;
-// }
-//
-// public void setContentRepository(ProvidedRepository contentRepository) {
-// this.contentRepository = contentRepository;
-// }
-
@Override
public Locale getDefaultLocale() {
return defaultLocale;
}
+ @Override
+ public UUID timeUUID() {
+ return uuidFactory.timeUUID();
+ }
+
@Override
public List<Locale> getLocales() {
return locales;
return availableSince != null;
}
- @Override
public CmsState getCmsState() {
return cmsState;
}
+ @Override
+ public CmsEventBus getCmsEventBus() {
+ return cmsEventBus;
+ }
+
+ public void setCmsEventBus(CmsEventBus cmsEventBus) {
+ this.cmsEventBus = cmsEventBus;
+ }
+
/*
* STATIC
*/
}
/** Required by SPNEGO login module. */
- public static GSSCredential getAcceptorCredentials() {
+ public GSSCredential getAcceptorCredentials() {
// TODO find a cleaner way
- return ((CmsUserAdmin) getInstance().userAdmin).getAcceptorCredentials();
+ return ((CmsUserAdmin) userAdmin).getAcceptorCredentials();
}
private static void setInstance(CmsContextImpl cmsContextImpl) {
-// if (cmsContextImpl != null) {
-// if (instance != null)
-// throw new IllegalStateException("CMS Context is already set");
-// instance = cmsContextImpl;
-// } else {
-// instance = null;
-// }
-// CmsContextImpl.class.notifyAll();
-
if (cmsContextImpl != null) {
if (instance.isDone())
throw new IllegalStateException("CMS Context is already set");
instance.complete(cmsContextImpl);
} else {
+ if (!instance.isDone())
+ instance.cancel(true);
instance = new CompletableFuture<CmsContextImpl>();
}
}
private static CmsContextImpl getInstance() {
-// while (instance == null) {
-// try {
-// CmsContextImpl.class.wait();
-// } catch (InterruptedException e) {
-// throw new IllegalStateException("Cannot wait for CMS context instance", e);
-// }
-// }
-// return instance;
-
try {
return instance.get();
} catch (InterruptedException | ExecutionException e) {
return cmsSessionsByLocalId.get(localId);
}
- /*
- * CMS Events
- */
- public void sendEvent(String topic, Map<String, Object> event) {
- SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
- if (publisher == null)
- return; // no one is interested
- publisher.submit(event);
- }
-
- public void addEventSubscriber(String topic, CmsEventSubscriber subscriber) {
- synchronized (topics) {
- if (!topics.containsKey(topic))
- topics.put(topic, new SubmissionPublisher<>());
- }
- SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
- CmsEventFlowSubscriber flowSubscriber = new CmsEventFlowSubscriber(topic, subscriber);
- publisher.subscribe(flowSubscriber);
- }
-
- public void removeEventSubscriber(String topic, CmsEventSubscriber subscriber) {
- SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
- if (publisher == null) {
- log.error("There should be an event topic " + topic);
- return;
- }
- for (Flow.Subscriber<? super Map<String, Object>> flowSubscriber : publisher.getSubscribers()) {
- if (flowSubscriber instanceof CmsEventFlowSubscriber)
- ((CmsEventFlowSubscriber) flowSubscriber).unsubscribe();
- }
- synchronized (topics) {
- if (!publisher.hasSubscribers()) {
- publisher.close();
- topics.remove(topic);
- }
- }
- }
-
- static class CmsEventFlowSubscriber implements Flow.Subscriber<Map<String, Object>> {
- private String topic;
- private CmsEventSubscriber eventSubscriber;
-
- private Subscription subscription;
-
- public CmsEventFlowSubscriber(String topic, CmsEventSubscriber eventSubscriber) {
- this.topic = topic;
- this.eventSubscriber = eventSubscriber;
- }
-
- @Override
- public void onSubscribe(Subscription subscription) {
- this.subscription = subscription;
- subscription.request(Long.MAX_VALUE);
- }
-
- @Override
- public void onNext(Map<String, Object> item) {
- eventSubscriber.onEvent(topic, item);
-
- }
-
- @Override
- public void onError(Throwable throwable) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onComplete() {
- // TODO Auto-generated method stub
-
- }
-
- void unsubscribe() {
- if (subscription != null)
- subscription.cancel();
- else
- throw new IllegalStateException("No subscription to cancel");
- }
-
- }
-
}