From: Mathieu Baudier Date: Thu, 16 Jun 2022 10:27:40 +0000 (+0200) Subject: Improve ACR. X-Git-Tag: v2.3.10~184 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=da9d144b6b241e1526a3bd255dff905a7969a5bc;p=lgpl%2Fargeo-commons.git Improve ACR. --- diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java index 63bf8dfff..116e45c7b 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java @@ -222,6 +222,14 @@ public class JcrContent extends AbstractContent { } + boolean exists() { + try { + return provider.getJcrSession(session, jcrWorkspace).itemExists(jcrPath); + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether " + jcrPath + " exists", e); + } + } + /* * ADAPTERS */ diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java index 198a2a3ed..235e27d1a 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java @@ -12,6 +12,7 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.xml.namespace.NamespaceContext; +import org.argeo.api.acr.Content; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.api.acr.spi.ProvidedSession; @@ -47,12 +48,19 @@ public class JcrContentProvider implements ContentProvider, NamespaceContext { } @Override - public ProvidedContent get(ProvidedSession contentSession, String mountPath, String relativePath) { + public ProvidedContent get(ProvidedSession contentSession, String relativePath) { String jcrWorkspace = ContentUtils.getParentPath(mountPath)[1]; String jcrPath = "/" + relativePath; return new JcrContent(contentSession, this, jcrWorkspace, jcrPath); } + @Override + public boolean exists(ProvidedSession contentSession, String relativePath) { + String jcrWorkspace = ContentUtils.getParentPath(mountPath)[1]; + String jcrPath = "/" + relativePath; + return new JcrContent(contentSession, this, jcrWorkspace, jcrPath).exists(); + } + public Session getJcrSession(ProvidedSession contentSession, String jcrWorkspace) { JcrSessionAdapter sessionAdapter = sessionAdapters.get(contentSession); if (sessionAdapter == null) { @@ -67,6 +75,10 @@ public class JcrContentProvider implements ContentProvider, NamespaceContext { return jcrSession; } + public Session getJcrSession(Content content, String jcrWorkspace) { + return getJcrSession(((ProvidedContent) content).getSession(), jcrWorkspace); + } + @Override public String getMountPath() { return mountPath; diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java index 4473498c1..5f2377be5 100644 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java +++ b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java @@ -11,7 +11,6 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; /** Stateless factory building an SWT user interface given a JCR context. */ -@FunctionalInterface public interface CmsUiProvider extends SwtUiProvider { /** * Initialises a user interface. @@ -19,7 +18,10 @@ public interface CmsUiProvider extends SwtUiProvider { * @param parent the parent composite * @param context a context node (holding the JCR underlying session), or null */ - Control createUi(Composite parent, Node context) throws RepositoryException; + default Control createUi(Composite parent, Node context) throws RepositoryException { + // does nothing by default + return null; + } default Control createUiPart(Composite parent, Node context) { try { @@ -37,7 +39,12 @@ public interface CmsUiProvider extends SwtUiProvider { Node node = ((JcrContent) context).getJcrNode(); return createUiPart(parent, node); } else { - throw new IllegalArgumentException("Content " + context + " is not compatible with JCR"); +// CmsLog.getLog(CmsUiProvider.class) +// .warn("In " + getClass() + ", content " + context + " is not compatible with JCR."); +// return createUiPart(parent, (Node) null); + + throw new IllegalArgumentException( + "In " + getClass() + ", content " + context + " is not compatible with JCR"); } } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java index 3adde724b..b7d37dc10 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java @@ -13,6 +13,8 @@ public interface ContentSession extends NamespaceContext { Locale getLocale(); Content get(String path); + + boolean exists(String path); CompletionStage edit(Consumer work); } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java index 850760134..72aa162b3 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java @@ -6,7 +6,9 @@ import javax.xml.namespace.NamespaceContext; public interface ContentProvider extends NamespaceContext { - ProvidedContent get(ProvidedSession session, String mountPath, String relativePath); + ProvidedContent get(ProvidedSession session, String relativePath); + + boolean exists(ProvidedSession session, String relativePath); String getMountPath(); diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java index 075f7bd1b..ba915ac40 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java @@ -1,6 +1,7 @@ package org.argeo.api.acr.spi; import java.util.Iterator; +import java.util.UUID; import java.util.concurrent.CompletionStage; import org.argeo.api.acr.Content; @@ -18,6 +19,10 @@ public interface ProvidedSession extends ContentSession { void notifyModification(ProvidedContent content); + UUID getUuid(); + + Content getSessionRunDir(); + /* * NAMESPACE CONTEXT */ diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsContext.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsContext.java index 8f4161c50..05108beac 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsContext.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsContext.java @@ -27,4 +27,6 @@ public interface CmsContext { /** Get the CMS session of this subject. */ CmsSession getCmsSession(Subject subject); + + CmsState getCmsState(); } diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java index ed8698fca..3828b080b 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java @@ -1,9 +1,12 @@ package org.argeo.api.cms; +import java.util.UUID; + /** A running node process. */ public interface CmsState { String getHostname(); Long getAvailableSince(); + UUID getUuid(); } diff --git a/org.argeo.cms/OSGI-INF/cmsContext.xml b/org.argeo.cms/OSGI-INF/cmsContext.xml index 63d43192e..bec003350 100644 --- a/org.argeo.cms/OSGI-INF/cmsContext.xml +++ b/org.argeo.cms/OSGI-INF/cmsContext.xml @@ -7,4 +7,6 @@ + + diff --git a/org.argeo.cms/OSGI-INF/cmsState.xml b/org.argeo.cms/OSGI-INF/cmsState.xml index a81e9f068..71dc6d4db 100644 --- a/org.argeo.cms/OSGI-INF/cmsState.xml +++ b/org.argeo.cms/OSGI-INF/cmsState.xml @@ -4,4 +4,5 @@ + diff --git a/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java index 6ca7b3591..cfffea027 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java @@ -22,7 +22,6 @@ import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.api.acr.spi.ProvidedRepository; import org.argeo.api.cms.CmsLog; -import org.argeo.api.uuid.UuidFactory; import org.argeo.cms.acr.xml.DomContentProvider; import org.argeo.cms.acr.xml.DomUtils; import org.w3c.dom.DOMException; @@ -36,8 +35,6 @@ import org.xml.sax.SAXException; public abstract class AbstractContentRepository implements ProvidedRepository { private final static CmsLog log = CmsLog.getLog(AbstractContentRepository.class); - private UuidFactory uuidFactory; - private MountManager mountManager; private TypesManager typesManager; @@ -199,9 +196,4 @@ public abstract class AbstractContentRepository implements ProvidedRepository { TypesManager getTypesManager() { return typesManager; } - - public void setUuidFactory(UuidFactory uuidFactory) { - this.uuidFactory = uuidFactory; - } - } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java index c2d6b21e4..46222ce1a 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java @@ -12,6 +12,8 @@ import org.argeo.api.acr.ContentSession; import org.argeo.api.acr.spi.ProvidedRepository; import org.argeo.api.cms.CmsAuth; import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.CmsState; +import org.argeo.api.uuid.UuidFactory; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.internal.runtime.CmsContextImpl; @@ -19,9 +21,13 @@ import org.argeo.cms.internal.runtime.CmsContextImpl; * Multi-session {@link ProvidedRepository}, integrated with a CMS. */ public class CmsContentRepository extends AbstractContentRepository { + public final static String RUN_BASE = "/run"; private Map userSessions = Collections.synchronizedMap(new HashMap<>()); + private CmsState cmsState; + private UuidFactory uuidFactory; + /* * REPOSITORY */ @@ -37,7 +43,8 @@ public class CmsContentRepository extends AbstractContentRepository { CmsSession cmsSession = CurrentUser.getCmsSession(); CmsContentSession contentSession = userSessions.get(cmsSession); if (contentSession == null) { - final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.getSubject(), locale); + final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.getUuid(), + cmsSession.getSubject(), locale); cmsSession.addOnCloseCallback((c) -> { newContentSession.close(); userSessions.remove(cmsSession); @@ -57,7 +64,20 @@ public class CmsContentRepository extends AbstractContentRepository { throw new RuntimeException("Could not login as data admin", e1); } finally { } - return new CmsContentSession(this, loginContext.getSubject(), Locale.getDefault()); + return new CmsContentSession(this, getCmsState().getUuid(), loginContext.getSubject(), + Locale.getDefault()); + } + + protected CmsState getCmsState() { + return cmsState; + } + + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; + } + + public void setUuidFactory(UuidFactory uuidFactory) { + this.uuidFactory = uuidFactory; } } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java index 5e5fb272c..025585634 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java @@ -5,6 +5,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Consumer; @@ -14,6 +15,7 @@ import javax.security.auth.Subject; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.CrName; import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; @@ -25,6 +27,7 @@ import org.argeo.cms.acr.xml.DomContentProvider; class CmsContentSession implements ProvidedSession { final private AbstractContentRepository contentRepository; + private final UUID uuid; private Subject subject; private Locale locale; @@ -34,14 +37,21 @@ class CmsContentSession implements ProvidedSession { private Set modifiedProviders = new TreeSet<>(); - public CmsContentSession(AbstractContentRepository contentRepository, Subject subject, Locale locale) { + private Content sessionRunDir; + + public CmsContentSession(AbstractContentRepository contentRepository, UUID uuid, Subject subject, Locale locale) { this.contentRepository = contentRepository; this.subject = subject; this.locale = locale; + this.uuid = uuid; + } public void close() { closed.complete(this); + + if (sessionRunDir != null) + sessionRunDir.remove(); } @Override @@ -53,11 +63,24 @@ class CmsContentSession implements ProvidedSession { public Content get(String path) { ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path); String mountPath = contentProvider.getMountPath(); + String relativePath = extractRelativePath(mountPath, path); + ProvidedContent content = contentProvider.get(CmsContentSession.this, relativePath); + return content; + } + + @Override + public boolean exists(String path) { + ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path); + String mountPath = contentProvider.getMountPath(); + String relativePath = extractRelativePath(mountPath, path); + return contentProvider.exists(this, relativePath); + } + + private String extractRelativePath(String mountPath, String path) { String relativePath = path.substring(mountPath.length()); if (relativePath.length() > 0 && relativePath.charAt(0) == '/') relativePath = relativePath.substring(1); - ProvidedContent content = contentProvider.get(CmsContentSession.this, mountPath, relativePath); - return content; + return relativePath; } @Override @@ -130,6 +153,26 @@ class CmsContentSession implements ProvidedSession { modifiedProviders.add(contentProvider); } + @Override + public UUID getUuid() { + return uuid; + } + + @Override + public Content getSessionRunDir() { + if (sessionRunDir == null) { + String runDirPath = CmsContentRepository.RUN_BASE + '/' + uuid.toString(); + if (exists(runDirPath)) + sessionRunDir = get(runDirPath); + else { + Content runDir = get(CmsContentRepository.RUN_BASE); + // TODO deal with no run dir available? + sessionRunDir = runDir.add(uuid.toString(), CrName.COLLECTION.get()); + } + } + return sessionRunDir; + } + // @Override // public String findNamespace(String prefix) { // return prefixes.get(prefix); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java index 2dcaeafaa..5609cf777 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java @@ -6,6 +6,7 @@ import java.util.function.BiConsumer; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; +import org.argeo.api.cms.CmsSession; /** Utilities and routines around {@link Content}. */ public class ContentUtils { diff --git a/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java index 09efa7274..cac407495 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Locale; import java.util.Objects; +import java.util.UUID; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; @@ -21,6 +22,8 @@ public class SingleUserContentRepository extends AbstractContentRepository { private final Subject subject; private final Locale locale; + private UUID uuid; + // the single session private CmsContentSession contentSession; @@ -34,6 +37,9 @@ public class SingleUserContentRepository extends AbstractContentRepository { this.subject = subject; this.locale = locale; + + // TODO use an UUID factory + this.uuid = UUID.randomUUID(); } @Override @@ -45,7 +51,7 @@ public class SingleUserContentRepository extends AbstractContentRepository { initRootContentProvider(null); if (contentSession != null) throw new IllegalStateException("Repository is already started, stop it first."); - contentSession = new CmsContentSession(this, subject, locale); + contentSession = new CmsContentSession(this, uuid, subject, locale); } @Override @@ -70,7 +76,7 @@ public class SingleUserContentRepository extends AbstractContentRepository { @Override protected CmsContentSession newSystemSession() { - return new CmsContentSession(this, subject, Locale.getDefault()); + return new CmsContentSession(this, uuid, subject, Locale.getDefault()); } public static void main(String... args) { diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java index e71acdabd..5c9c1096e 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java @@ -213,7 +213,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { QName[] classes = null; ContentProvider contentProvider = session.getRepository().getMountContentProvider(fsContent, false, classes); - Content mountedContent = contentProvider.get(session, fsContent.getPath(), ""); + Content mountedContent = contentProvider.get(session, ""); return mountedContent; } else { return (Content) fsContent; @@ -247,7 +247,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { if (session.getRepository().shouldMount(classes)) { ContentProvider contentProvider = session.getRepository().getMountContentProvider(fsContent, true, classes); - Content mountedContent = contentProvider.get(session, fsContent.getPath(), ""); + Content mountedContent = contentProvider.get(session, ""); fsContent.put(CrName.MOUNT.get(), "true"); return mountedContent; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java index 62b20af3d..9d696b07f 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java @@ -33,7 +33,7 @@ public class FsContentProvider implements ContentProvider { public FsContentProvider(String mountPath, Path rootPath) { Objects.requireNonNull(mountPath); Objects.requireNonNull(rootPath); - + this.mountPath = mountPath; this.rootPath = rootPath; // FIXME make it more robust @@ -112,7 +112,7 @@ public class FsContentProvider implements ContentProvider { } @Override - public ProvidedContent get(ProvidedSession session, String mountPath, String relativePath) { + public ProvidedContent get(ProvidedSession session, String relativePath) { return new FsContent(session, this, rootPath.resolve(relativePath)); } @@ -120,6 +120,11 @@ public class FsContentProvider implements ContentProvider { * NAMESPACE CONTEXT */ + @Override + public boolean exists(ProvidedSession session, String relativePath) { + return Files.exists(rootPath.resolve(relativePath)); + } + @Override public String getNamespaceURI(String prefix) { return NamespaceUtils.getNamespaceURI((p) -> prefixes.get(p), prefix); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java index 423b60ead..54013e2ad 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java @@ -61,27 +61,40 @@ public class DomContentProvider implements ContentProvider, NamespaceContext { // } @Override - public ProvidedContent get(ProvidedSession session, String mountPath, String relativePath) { + public ProvidedContent get(ProvidedSession session, String relativePath) { if ("".equals(relativePath)) return new DomContent(session, this, document.getDocumentElement()); + + NodeList nodes = findContent(relativePath); + if (nodes.getLength() > 1) + throw new IllegalArgumentException("Multiple content found for " + relativePath + " under " + mountPath); + if (nodes.getLength() == 0) + throw new ContentNotFoundException("Path " + relativePath + " under " + mountPath + " was not found"); + Element element = (Element) nodes.item(0); + return new DomContent(session, this, element); + } + + protected NodeList findContent(String relativePath) { if (relativePath.startsWith("/")) throw new IllegalArgumentException("Relative path cannot start with /"); - String xPathExpression = '/' + relativePath; if ("/".equals(mountPath)) xPathExpression = "/cr:root" + xPathExpression; try { NodeList nodes = (NodeList) xPath.get().evaluate(xPathExpression, document, XPathConstants.NODESET); - if (nodes.getLength() > 1) - throw new IllegalArgumentException( - "Multiple content found for " + relativePath + " under " + mountPath); - if (nodes.getLength() == 0) - throw new ContentNotFoundException("Path " + relativePath + " under " + mountPath + " was not found"); - Element element = (Element) nodes.item(0); - return new DomContent(session, this, element); + return nodes; } catch (XPathExpressionException e) { throw new IllegalArgumentException("XPath expression " + xPathExpression + " cannot be evaluated", e); } + + } + + @Override + public boolean exists(ProvidedSession session, String relativePath) { + if ("".equals(relativePath)) + return true; + NodeList nodes = findContent(relativePath); + return nodes.getLength() != 0; } public void persist(ProvidedSession session) { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java index 928afc0d5..1efb93afd 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java @@ -141,7 +141,8 @@ class CmsAuthUtils { if (currentLocalSessionAnonymous) { currentLocalSession.close(); // new CMS session - cmsSession = new WebCmsSessionImpl(subject, authorization, locale, request); + UUID cmsSessionUuid = CmsContextImpl.getCmsContext().getUuidFactory().timeUUID(); + cmsSession = new WebCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request); CmsContextImpl.getCmsContext().registerCmsSession(cmsSession); } else if (!authorization.getName().equals(currentLocalSession.getAuthorization().getName())) { throw new IllegalStateException("Inconsistent user " + authorization.getName() @@ -163,7 +164,8 @@ class CmsAuthUtils { } } else { // new CMS session - cmsSession = new WebCmsSessionImpl(subject, authorization, locale, request); + UUID cmsSessionUuid = CmsContextImpl.getCmsContext().getUuidFactory().timeUUID(); + cmsSession = new WebCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request); CmsContextImpl.getCmsContext().registerCmsSession(cmsSession); } @@ -182,7 +184,8 @@ class CmsAuthUtils { } else { CmsSessionImpl cmsSession = CmsContextImpl.getCmsContext().getCmsSessionByLocalId(SINGLE_USER_LOCAL_ID); if (cmsSession == null) { - cmsSession = new CmsSessionImpl(subject, authorization, locale, SINGLE_USER_LOCAL_ID); + UUID cmsSessionUuid = CmsContextImpl.getCmsContext().getUuidFactory().timeUUID(); + cmsSession = new CmsSessionImpl(cmsSessionUuid, subject, authorization, locale, SINGLE_USER_LOCAL_ID); CmsContextImpl.getCmsContext().registerCmsSession(cmsSession); } CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid()); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java index a3670c0cb..164d319f1 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java @@ -6,13 +6,12 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -30,10 +29,6 @@ import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; import org.argeo.cms.internal.runtime.CmsContextImpl; import org.argeo.cms.security.NodeSecurityUtils; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.useradmin.Authorization; @@ -61,7 +56,10 @@ public class CmsSessionImpl implements CmsSession, Serializable { private List> onCloseCallbacks = Collections.synchronizedList(new ArrayList<>()); - public CmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, String localSessionId) { + public CmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale, + String localSessionId) { + Objects.requireNonNull(uuid); + this.creationTime = ZonedDateTime.now(); this.locale = locale; this.accessControlContext = Subject.doAs(initialSubject, new PrivilegedAction() { @@ -86,14 +84,7 @@ public class CmsSessionImpl implements CmsSession, Serializable { this.userDn = NodeSecurityUtils.ROLE_ANONYMOUS_NAME; this.anonymous = true; } - // TODO use time-based UUID? - this.uuid = UUID.randomUUID(); - // register as service -// Hashtable props = new Hashtable<>(); -// props.put(CmsSession.USER_DN, userDn.toString()); -// props.put(CmsSession.SESSION_UUID, uuid.toString()); -// props.put(CmsSession.SESSION_LOCAL_ID, localSessionId); -// serviceRegistration = bc.registerService(CmsSession.class, this, props); + this.uuid = uuid; } public void close() { @@ -210,57 +201,4 @@ public class CmsSessionImpl implements CmsSession, Serializable { public String toString() { return "CMS Session " + userDn + " localId=" + localSessionId + ", uuid=" + uuid; } - -// public static CmsSessionImpl getByLocalId(String localId) { -// Collection> sr; -// try { -// sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")"); -// } catch (InvalidSyntaxException e) { -// throw new IllegalArgumentException("Cannot get CMS session for id " + localId, e); -// } -// ServiceReference cmsSessionRef; -// if (sr.size() == 1) { -// cmsSessionRef = sr.iterator().next(); -// return (CmsSessionImpl) bc.getService(cmsSessionRef); -// } else if (sr.size() == 0) { -// return null; -// } else -// throw new IllegalStateException(sr.size() + " CMS sessions registered for " + localId); -// -// } -// -// public static CmsSessionImpl getByUuid(Object uuid) { -// Collection> sr; -// try { -// sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")"); -// } catch (InvalidSyntaxException e) { -// throw new IllegalArgumentException("Cannot get CMS session for uuid " + uuid, e); -// } -// ServiceReference cmsSessionRef; -// if (sr.size() == 1) { -// cmsSessionRef = sr.iterator().next(); -// return (CmsSessionImpl) bc.getService(cmsSessionRef); -// } else if (sr.size() == 0) { -// return null; -// } else -// throw new IllegalStateException(sr.size() + " CMS sessions registered for " + uuid); -// -// } -// -// public static void closeInvalidSessions() { -// Collection> srs; -// try { -// srs = bc.getServiceReferences(CmsSession.class, null); -// for (ServiceReference sr : srs) { -// CmsSession cmsSession = bc.getService(sr); -// if (!cmsSession.isValid()) { -// ((CmsSessionImpl) cmsSession).close(); -// if (log.isDebugEnabled()) -// log.debug("Closed expired CMS session " + cmsSession); -// } -// } -// } catch (InvalidSyntaxException e) { -// throw new IllegalArgumentException("Cannot get CMS sessions", e); -// } -// } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java index eb6c89d37..4b4a77641 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java @@ -1,6 +1,7 @@ package org.argeo.cms.internal.http; import java.util.Locale; +import java.util.UUID; import javax.security.auth.Subject; @@ -14,9 +15,9 @@ public class WebCmsSessionImpl extends CmsSessionImpl { private static final long serialVersionUID = -5178883380637048025L; private RemoteAuthSession httpSession; - public WebCmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, + public WebCmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale, RemoteAuthRequest request) { - super(initialSubject, authorization, locale, request.getSession().getId()); + super(uuid, initialSubject, authorization, locale, request.getSession().getId()); httpSession = request.getSession(); } @@ -26,8 +27,4 @@ public class WebCmsSessionImpl extends CmsSessionImpl { return false; return httpSession.isValid(); } - -// public static CmsSessionImpl getCmsSession(RemoteAuthRequest request) { -// return CmsSessionImpl.getByLocalId(request.getSession().getId()); -// } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java index 93ed319f1..10bda17fb 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java @@ -13,6 +13,8 @@ import java.util.concurrent.ExecutionException; import javax.security.auth.Subject; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.api.acr.spi.ProvidedRepository; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsContext; import org.argeo.api.cms.CmsDeployment; @@ -20,6 +22,7 @@ import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; import org.argeo.api.cms.CmsSessionId; import org.argeo.api.cms.CmsState; +import org.argeo.api.uuid.UuidFactory; import org.argeo.cms.LocaleUtils; import org.argeo.cms.internal.auth.CmsSessionImpl; import org.ietf.jgss.GSSCredential; @@ -35,6 +38,8 @@ public class CmsContextImpl implements CmsContext { private CmsState cmsState; private CmsDeployment cmsDeployment; private UserAdmin userAdmin; + private UuidFactory uuidFactory; + private ProvidedRepository contentRepository; // i18n private Locale defaultLocale; @@ -152,6 +157,22 @@ public class CmsContextImpl implements CmsContext { this.userAdmin = userAdmin; } + public UuidFactory getUuidFactory() { + return uuidFactory; + } + + public void setUuidFactory(UuidFactory uuidFactory) { + this.uuidFactory = uuidFactory; + } + + public ProvidedRepository getContentRepository() { + return contentRepository; + } + + public void setContentRepository(ProvidedRepository contentRepository) { + this.contentRepository = contentRepository; + } + @Override public Locale getDefaultLocale() { return defaultLocale; @@ -171,6 +192,11 @@ public class CmsContextImpl implements CmsContext { return availableSince != null; } + @Override + public CmsState getCmsState() { + return cmsState; + } + /* * STATIC */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java index a0c4b0c0b..41e326534 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java @@ -3,13 +3,14 @@ package org.argeo.cms.internal.runtime; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; +import java.util.UUID; import javax.security.auth.login.Configuration; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; +import org.argeo.api.uuid.UuidFactory; import org.argeo.cms.auth.ident.IdentClient; -import org.osgi.framework.Constants; /** * Implementation of a {@link CmsState}, initialising the required services. @@ -20,10 +21,12 @@ public class CmsStateImpl implements CmsState { // REFERENCES private Long availableSince; - private String stateUuid; + private UUID uuid; // private final boolean cleanState; private String hostname; + private UuidFactory uuidFactory; + public void start() { // Runtime.getRuntime().addShutdownHook(new CmsShutdown()); @@ -34,7 +37,9 @@ public class CmsStateImpl implements CmsState { if (log.isTraceEnabled()) log.trace("CMS State started"); - this.stateUuid = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID); +// String stateUuidStr = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID); +// this.uuid = UUID.fromString(stateUuidStr); + this.uuid = uuidFactory.timeUUID(); // this.cleanState = stateUuid.equals(frameworkUuid); try { this.hostname = InetAddress.getLocalHost().getHostName(); @@ -46,7 +51,7 @@ public class CmsStateImpl implements CmsState { if (log.isDebugEnabled()) // log.debug("## CMS starting... stateUuid=" + this.stateUuid + (cleanState ? " // (clean state) " : " ")); - log.debug("## CMS starting... (" + stateUuid + ")"); + log.debug("## CMS starting... (" + uuid + ")"); // initI18n(); // initServices(); @@ -70,13 +75,12 @@ public class CmsStateImpl implements CmsState { public void stop() { if (log.isDebugEnabled()) - log.debug("CMS stopping... (" + this.stateUuid + ")"); + log.debug("CMS stopping... (" + this.uuid + ")"); long duration = ((System.currentTimeMillis() - availableSince) / 1000) / 60; log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##"); } - @Override public Long getAvailableSince() { return availableSince; @@ -89,6 +93,15 @@ public class CmsStateImpl implements CmsState { return hostname; } + @Override + public UUID getUuid() { + return uuid; + } + + public void setUuidFactory(UuidFactory uuidFactory) { + this.uuidFactory = uuidFactory; + } + /* * STATIC */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java index 7d3eb283b..cff9bc514 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java @@ -1,27 +1,36 @@ package org.argeo.cms.internal.runtime; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import org.argeo.api.acr.spi.ContentProvider; -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsState; import org.argeo.cms.acr.CmsContentRepository; import org.argeo.cms.acr.fs.FsContentProvider; +import org.argeo.util.OS; public class DeployedContentRepository extends CmsContentRepository { private final static String ROOT_XML = "cr:root.xml"; - private CmsState cmsState; @Override public void start() { - super.start(); - Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML); - initRootContentProvider(null); + try { + super.start(); + Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML); + initRootContentProvider(null); // Path srvPath = KernelUtils.getOsgiInstancePath(CmsConstants.SRV_WORKSPACE); // FsContentProvider srvContentProvider = new FsContentProvider("/" + CmsConstants.SRV_WORKSPACE, srvPath, false); // addProvider(srvContentProvider); + + Path runDirPath = KernelUtils.getOsgiInstancePath(CmsContentRepository.RUN_BASE); + Files.createDirectories(runDirPath); + FsContentProvider runContentProvider = new FsContentProvider(CmsContentRepository.RUN_BASE, runDirPath); + addProvider(runContentProvider); + } catch (IOException e) { + throw new IllegalStateException("Cannot start content repository", e); + } } @Override @@ -37,8 +46,4 @@ public class DeployedContentRepository extends CmsContentRepository { public void removeContentProvider(ContentProvider provider, Map properties) { } - public void setCmsState(CmsState cmsState) { - this.cmsState = cmsState; - } - } diff --git a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java index 27b23233f..e4087e139 100644 --- a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java +++ b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java @@ -35,12 +35,19 @@ public class StaticCms { private CompletableFuture stopped = new CompletableFuture(); public void start() { + // UID factory + CmsUuidFactory uuidFactory = new CmsUuidFactory(); + Component uuidFactoryC = new Component.Builder<>(uuidFactory) // + .addType(UuidFactory.class) // + .build(register); + // CMS State CmsStateImpl cmsState = new CmsStateImpl(); Component cmsStateC = new Component.Builder<>(cmsState) // .addType(CmsState.class) // .addActivation(cmsState::start) // .addDeactivation(cmsState::stop) // + .addDependency(uuidFactoryC.getType(UuidFactory.class), cmsState::setUuidFactory, null) // .build(register); // Deployment Configuration @@ -81,6 +88,17 @@ public class StaticCms { }, null) // .build(register); + // Content Repository + DeployedContentRepository contentRepository = new DeployedContentRepository(); + Component contentRepositoryC = new Component.Builder<>(contentRepository) // + .addType(ProvidedRepository.class) // + .addType(ContentRepository.class) // + .addActivation(contentRepository::start) // + .addDeactivation(contentRepository::stop) // + .addDependency(cmsStateC.getType(CmsState.class), contentRepository::setCmsState, null) // + .addDependency(uuidFactoryC.getType(UuidFactory.class), contentRepository::setUuidFactory, null) // + .build(register); + // CMS Context CmsContextImpl cmsContext = new CmsContextImpl(); Component cmsContextC = new Component.Builder<>(cmsContext) // @@ -90,26 +108,12 @@ public class StaticCms { .addDependency(cmsStateC.getType(CmsState.class), cmsContext::setCmsState, null) // .addDependency(cmsDeploymentC.getType(CmsDeployment.class), cmsContext::setCmsDeployment, null) // .addDependency(userAdminC.getType(UserAdmin.class), cmsContext::setUserAdmin, null) // + .addDependency(uuidFactoryC.getType(UuidFactory.class), cmsContext::setUuidFactory, null) // + .addDependency(contentRepositoryC.getType(ProvidedRepository.class), cmsContext::setContentRepository, + null) // .build(register); assert cmsContextC.get() == cmsContext; - // UID factory - CmsUuidFactory uuidFactory = new CmsUuidFactory(); - Component uuidFactoryC = new Component.Builder<>(uuidFactory) // - .addType(UuidFactory.class) // - .build(register); - - // Content Repository - DeployedContentRepository contentRepository = new DeployedContentRepository(); - Component contentRepositoryC = new Component.Builder<>(contentRepository) // - .addType(ProvidedRepository.class) // - .addType(ContentRepository.class) // - .addActivation(contentRepository::start) // - .addDeactivation(contentRepository::stop) // - .addDependency(cmsStateC.getType(CmsState.class), contentRepository::setCmsState, null) // - .addDependency(uuidFactoryC.getType(UuidFactory.class), contentRepository::setUuidFactory, null) // - .build(register); - register.activate(); }