1 package org
.argeo
.cms
.acr
;
3 import java
.io
.IOException
;
5 import java
.nio
.charset
.StandardCharsets
;
6 import java
.nio
.file
.Files
;
7 import java
.nio
.file
.Path
;
8 import java
.util
.Collections
;
9 import java
.util
.HashMap
;
10 import java
.util
.Iterator
;
11 import java
.util
.Locale
;
13 import java
.util
.NavigableMap
;
15 import java
.util
.TreeMap
;
16 import java
.util
.concurrent
.CompletableFuture
;
17 import java
.util
.concurrent
.CompletionStage
;
18 import java
.util
.stream
.Collectors
;
20 import javax
.security
.auth
.Subject
;
21 import javax
.security
.auth
.login
.LoginContext
;
22 import javax
.security
.auth
.login
.LoginException
;
23 import javax
.xml
.parsers
.DocumentBuilder
;
24 import javax
.xml
.parsers
.DocumentBuilderFactory
;
25 import javax
.xml
.parsers
.ParserConfigurationException
;
26 import javax
.xml
.transform
.Transformer
;
27 import javax
.xml
.transform
.TransformerException
;
28 import javax
.xml
.transform
.TransformerFactory
;
29 import javax
.xml
.transform
.TransformerFactoryConfigurationError
;
30 import javax
.xml
.transform
.dom
.DOMSource
;
31 import javax
.xml
.transform
.stream
.StreamResult
;
32 import javax
.xml
.validation
.Schema
;
34 import org
.argeo
.api
.acr
.Content
;
35 import org
.argeo
.api
.acr
.ContentSession
;
36 import org
.argeo
.api
.acr
.ContentUtils
;
37 import org
.argeo
.api
.acr
.CrName
;
38 import org
.argeo
.api
.acr
.NamespaceUtils
;
39 import org
.argeo
.api
.acr
.spi
.ContentProvider
;
40 import org
.argeo
.api
.acr
.spi
.ProvidedRepository
;
41 import org
.argeo
.api
.acr
.spi
.ProvidedSession
;
42 import org
.argeo
.api
.cms
.CmsAuth
;
43 import org
.argeo
.api
.cms
.CmsLog
;
44 import org
.argeo
.api
.cms
.CmsSession
;
45 import org
.argeo
.cms
.acr
.xml
.DomContentProvider
;
46 import org
.argeo
.cms
.auth
.CurrentUser
;
47 import org
.argeo
.cms
.internal
.runtime
.CmsContextImpl
;
48 import org
.w3c
.dom
.DOMException
;
49 import org
.w3c
.dom
.Document
;
50 import org
.w3c
.dom
.Element
;
51 import org
.xml
.sax
.ErrorHandler
;
52 import org
.xml
.sax
.InputSource
;
53 import org
.xml
.sax
.SAXException
;
54 import org
.xml
.sax
.SAXParseException
;
57 * Base implementation of a {@link ProvidedRepository} integrated with a CMS.
59 public class CmsContentRepository
implements ProvidedRepository
{
60 private final static CmsLog log
= CmsLog
.getLog(CmsContentRepository
.class);
62 private NavigableMap
<String
, ContentProvider
> partitions
= new TreeMap
<>();
65 private NavigableMap
<String
, String
> prefixes
= new TreeMap
<>();
67 private Schema schema
;
69 private CmsContentSession systemSession
;
71 private Map
<CmsSession
, CmsContentSession
> userSessions
= Collections
.synchronizedMap(new HashMap
<>());
73 public CmsContentRepository() {
74 prefixes
.put(CrName
.CR_DEFAULT_PREFIX
, CrName
.CR_NAMESPACE_URI
);
75 prefixes
.put("basic", CrName
.CR_NAMESPACE_URI
);
76 prefixes
.put("owner", CrName
.CR_NAMESPACE_URI
);
77 prefixes
.put("posix", CrName
.CR_NAMESPACE_URI
);
79 systemSession
= newSystemSession();
82 protected CmsContentSession
newSystemSession() {
83 LoginContext loginContext
;
85 loginContext
= new LoginContext(CmsAuth
.DATA_ADMIN
.getLoginContextName());
87 } catch (LoginException e1
) {
88 throw new RuntimeException("Could not login as data admin", e1
);
91 return new CmsContentSession(loginContext
.getSubject(), Locale
.getDefault());
106 public ContentSession
get() {
107 return get(CmsContextImpl
.getCmsContext().getDefaultLocale());
111 public ContentSession
get(Locale locale
) {
112 // Subject subject = Subject.getSubject(AccessController.getContext());
113 CmsSession cmsSession
= CurrentUser
.getCmsSession();
114 CmsContentSession contentSession
= userSessions
.get(cmsSession
);
115 if (contentSession
== null) {
116 final CmsContentSession newContentSession
= new CmsContentSession(cmsSession
.getSubject(), locale
);
117 cmsSession
.addOnCloseCallback((c
) -> {
118 newContentSession
.close();
119 userSessions
.remove(cmsSession
);
121 contentSession
= newContentSession
;
123 return contentSession
;
126 public void addProvider(String base
, ContentProvider provider
) {
127 partitions
.put(base
, provider
);
128 if ("/".equals(base
))// root
130 String
[] parentPath
= ContentUtils
.getParentPath(base
);
131 Content parent
= systemSession
.get(parentPath
[0]);
132 Content mount
= parent
.add(parentPath
[1]);
133 // TODO use a boolean
134 // ContentName name = new ContentName(CrName.MOUNT.getNamespaceURI(),
135 // CrName.MOUNT.name(), systemSession);
136 mount
.put(CrName
.MOUNT
.get(), "true");
139 public void registerPrefix(String prefix
, String namespaceURI
) {
140 String registeredUri
= prefixes
.get(prefix
);
141 if (registeredUri
== null) {
142 prefixes
.put(prefix
, namespaceURI
);
145 if (!registeredUri
.equals(namespaceURI
))
146 throw new IllegalStateException("Prefix " + prefix
+ " is already registred for " + registeredUri
);
147 // do nothing if same namespace is already registered
153 public void initRootContentProvider(Path path
) {
155 DocumentBuilderFactory factory
= DocumentBuilderFactory
.newInstance();
156 factory
.setNamespaceAware(true);
157 factory
.setXIncludeAware(true);
158 // factory.setSchema(schema);
160 DocumentBuilder dBuilder
= factory
.newDocumentBuilder();
161 dBuilder
.setErrorHandler(new ErrorHandler() {
164 public void warning(SAXParseException exception
) throws SAXException
{
168 public void fatalError(SAXParseException exception
) throws SAXException
{
172 public void error(SAXParseException exception
) throws SAXException
{
173 log
.error(exception
);
179 if (Files
.exists(path
)) {
180 InputSource inputSource
= new InputSource(path
.toAbsolutePath().toUri().toString());
181 inputSource
.setEncoding(StandardCharsets
.UTF_8
.name());
182 // TODO public id as well?
183 document
= dBuilder
.parse(inputSource
);
185 document
= dBuilder
.newDocument();
186 // Element root = document.createElementNS(CrName.ROOT.getNamespaceURI(),
187 // CrName.ROOT.get().toPrefixedString());
188 Element root
= document
.createElement(CrName
.ROOT
.get().toPrefixedString());
189 // root.setAttribute("xmlns", "");
190 root
.setAttribute("xmlns:" + CrName
.CR_DEFAULT_PREFIX
, CrName
.CR_NAMESPACE_URI
);
191 document
.appendChild(root
);
194 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
195 Transformer transformer
= transformerFactory
.newTransformer();
196 DOMSource source
= new DOMSource(document
);
197 try (Writer writer
= Files
.newBufferedWriter(path
, StandardCharsets
.UTF_8
)) {
198 StreamResult result
= new StreamResult(writer
);
199 transformer
.transform(source
, result
);
203 DomContentProvider contentProvider
= new DomContentProvider(document
);
204 addProvider("/", contentProvider
);
205 } catch (DOMException
| ParserConfigurationException
| SAXException
| IOException
206 | TransformerFactoryConfigurationError
| TransformerException e
) {
207 throw new IllegalStateException("Cannot init ACR root " + path
, e
);
220 class CmsContentSession
implements ProvidedSession
{
221 private Subject subject
;
222 private Locale locale
;
224 private CompletableFuture
<ProvidedSession
> closed
= new CompletableFuture
<>();
226 public CmsContentSession(Subject subject
, Locale locale
) {
227 this.subject
= subject
;
228 this.locale
= locale
;
231 public void close() {
232 closed
.complete(this);
236 public CompletionStage
<ProvidedSession
> onClose() {
237 return closed
.minimalCompletionStage();
241 public Content
get(String path
) {
242 Map
.Entry
<String
, ContentProvider
> entry
= partitions
.floorEntry(path
);
243 String mountPath
= entry
.getKey();
244 ContentProvider provider
= entry
.getValue();
245 String relativePath
= path
.substring(mountPath
.length());
246 return provider
.get(CmsContentSession
.this, mountPath
, relativePath
);
250 public Subject
getSubject() {
255 public Locale
getLocale() {
260 public ProvidedRepository
getRepository() {
261 return CmsContentRepository
.this;
269 public String
getNamespaceURI(String prefix
) {
270 return NamespaceUtils
.getNamespaceURI((p
) -> prefixes
.get(p
), prefix
);
274 public Iterator
<String
> getPrefixes(String namespaceURI
) {
275 return NamespaceUtils
.getPrefixes((ns
) -> prefixes
.entrySet().stream().filter(e
-> e
.getValue().equals(ns
))
276 .map(Map
.Entry
::getKey
).collect(Collectors
.toUnmodifiableSet()), namespaceURI
);
280 // public String findNamespace(String prefix) {
281 // return prefixes.get(prefix);
285 // public Set<String> findPrefixes(String namespaceURI) {
286 // Set<String> res = prefixes.entrySet().stream().filter(e -> e.getValue().equals(namespaceURI))
287 // .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet());
293 // public String findPrefix(String namespaceURI) {
294 // if (CrName.CR_NAMESPACE_URI.equals(namespaceURI) && prefixes.containsKey(CrName.CR_DEFAULT_PREFIX))
295 // return CrName.CR_DEFAULT_PREFIX;
296 // return ProvidedSession.super.findPrefix(namespaceURI);