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
;
14 import java
.util
.TreeMap
;
15 import java
.util
.concurrent
.CompletableFuture
;
16 import java
.util
.concurrent
.CompletionStage
;
17 import java
.util
.stream
.Collectors
;
19 import javax
.security
.auth
.Subject
;
20 import javax
.security
.auth
.login
.LoginContext
;
21 import javax
.security
.auth
.login
.LoginException
;
22 import javax
.xml
.XMLConstants
;
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
;
33 import org
.argeo
.api
.acr
.Content
;
34 import org
.argeo
.api
.acr
.ContentSession
;
35 import org
.argeo
.api
.acr
.ContentUtils
;
36 import org
.argeo
.api
.acr
.CrName
;
37 import org
.argeo
.api
.acr
.NamespaceUtils
;
38 import org
.argeo
.api
.acr
.spi
.ContentProvider
;
39 import org
.argeo
.api
.acr
.spi
.ProvidedRepository
;
40 import org
.argeo
.api
.acr
.spi
.ProvidedSession
;
41 import org
.argeo
.api
.cms
.CmsAuth
;
42 import org
.argeo
.api
.cms
.CmsLog
;
43 import org
.argeo
.api
.cms
.CmsSession
;
44 import org
.argeo
.cms
.acr
.xml
.DomContentProvider
;
45 import org
.argeo
.cms
.auth
.CurrentUser
;
46 import org
.argeo
.cms
.internal
.runtime
.CmsContextImpl
;
47 import org
.w3c
.dom
.DOMException
;
48 import org
.w3c
.dom
.Document
;
49 import org
.w3c
.dom
.Element
;
50 import org
.xml
.sax
.ErrorHandler
;
51 import org
.xml
.sax
.InputSource
;
52 import org
.xml
.sax
.SAXException
;
53 import org
.xml
.sax
.SAXParseException
;
56 * Base implementation of a {@link ProvidedRepository} integrated with a CMS.
58 public class CmsContentRepository
implements ProvidedRepository
{
59 private final static CmsLog log
= CmsLog
.getLog(CmsContentRepository
.class);
61 private NavigableMap
<String
, ContentProvider
> partitions
= new TreeMap
<>();
64 // private NavigableMap<String, String> prefixes = new TreeMap<>();
66 // private Schema schema;
67 private ContentTypesManager contentTypesManager
;
69 private CmsContentSession systemSession
;
71 private Map
<CmsSession
, CmsContentSession
> userSessions
= Collections
.synchronizedMap(new HashMap
<>());
73 public CmsContentRepository() {
74 contentTypesManager
= new ContentTypesManager();
75 contentTypesManager
.init();
76 contentTypesManager
.listTypes();
78 systemSession
= newSystemSession();
81 protected CmsContentSession
newSystemSession() {
82 LoginContext loginContext
;
84 loginContext
= new LoginContext(CmsAuth
.DATA_ADMIN
.getLoginContextName());
86 } catch (LoginException e1
) {
87 throw new RuntimeException("Could not login as data admin", e1
);
90 return new CmsContentSession(loginContext
.getSubject(), Locale
.getDefault());
105 public ContentSession
get() {
106 return get(CmsContextImpl
.getCmsContext().getDefaultLocale());
110 public ContentSession
get(Locale locale
) {
111 // Subject subject = Subject.getSubject(AccessController.getContext());
112 CmsSession cmsSession
= CurrentUser
.getCmsSession();
113 CmsContentSession contentSession
= userSessions
.get(cmsSession
);
114 if (contentSession
== null) {
115 final CmsContentSession newContentSession
= new CmsContentSession(cmsSession
.getSubject(), locale
);
116 cmsSession
.addOnCloseCallback((c
) -> {
117 newContentSession
.close();
118 userSessions
.remove(cmsSession
);
120 contentSession
= newContentSession
;
122 return contentSession
;
125 public void addProvider(String base
, ContentProvider provider
) {
126 partitions
.put(base
, provider
);
127 if ("/".equals(base
))// root
129 String
[] parentPath
= ContentUtils
.getParentPath(base
);
130 Content parent
= systemSession
.get(parentPath
[0]);
131 Content mount
= parent
.add(parentPath
[1]);
132 // TODO use a boolean
133 // ContentName name = new ContentName(CrName.MOUNT.getNamespaceURI(),
134 // CrName.MOUNT.name(), systemSession);
135 mount
.put(CrName
.MOUNT
.get(), "true");
138 public void registerTypes(String prefix
, String namespaceURI
, String schemaSystemId
) {
139 contentTypesManager
.registerTypes(prefix
, namespaceURI
, schemaSystemId
);
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 factory
.setSchema(contentTypesManager
.getSchema());
162 DocumentBuilder dBuilder
= factory
.newDocumentBuilder();
163 dBuilder
.setErrorHandler(new ErrorHandler() {
166 public void warning(SAXParseException exception
) throws SAXException
{
170 public void fatalError(SAXParseException exception
) throws SAXException
{
174 public void error(SAXParseException exception
) throws SAXException
{
175 log
.error(exception
);
181 // if (path != null && Files.exists(path)) {
182 // InputSource inputSource = new InputSource(path.toAbsolutePath().toUri().toString());
183 // inputSource.setEncoding(StandardCharsets.UTF_8.name());
184 // // TODO public id as well?
185 // document = dBuilder.parse(inputSource);
187 document
= dBuilder
.newDocument();
188 Element root
= document
.createElementNS(CrName
.CR_NAMESPACE_URI
, CrName
.ROOT
.get().toPrefixedString());
190 for (String prefix
: contentTypesManager
.getPrefixes().keySet()) {
191 root
.setAttributeNS(XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
, XMLConstants
.XMLNS_ATTRIBUTE
+ ":" + prefix
,
192 contentTypesManager
.getPrefixes().get(prefix
));
195 document
.appendChild(root
);
199 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
200 Transformer transformer
= transformerFactory
.newTransformer();
201 DOMSource source
= new DOMSource(document
);
202 try (Writer writer
= Files
.newBufferedWriter(path
, StandardCharsets
.UTF_8
)) {
203 StreamResult result
= new StreamResult(writer
);
204 transformer
.transform(source
, result
);
209 DomContentProvider contentProvider
= new DomContentProvider(document
);
210 addProvider("/", contentProvider
);
211 } catch (DOMException
| ParserConfigurationException
| IOException
| TransformerFactoryConfigurationError
212 | TransformerException e
) {
213 throw new IllegalStateException("Cannot init ACR root " + path
, e
);
226 class CmsContentSession
implements ProvidedSession
{
227 private Subject subject
;
228 private Locale locale
;
230 private CompletableFuture
<ProvidedSession
> closed
= new CompletableFuture
<>();
232 public CmsContentSession(Subject subject
, Locale locale
) {
233 this.subject
= subject
;
234 this.locale
= locale
;
237 public void close() {
238 closed
.complete(this);
242 public CompletionStage
<ProvidedSession
> onClose() {
243 return closed
.minimalCompletionStage();
247 public Content
get(String path
) {
248 Map
.Entry
<String
, ContentProvider
> entry
= partitions
.floorEntry(path
);
250 throw new IllegalArgumentException("No entry provider found for " + path
);
251 String mountPath
= entry
.getKey();
252 ContentProvider provider
= entry
.getValue();
253 String relativePath
= path
.substring(mountPath
.length());
254 if (relativePath
.length() > 0 && relativePath
.charAt(0) == '/')
255 relativePath
= relativePath
.substring(1);
256 return provider
.get(CmsContentSession
.this, mountPath
, relativePath
);
260 public Subject
getSubject() {
265 public Locale
getLocale() {
270 public ProvidedRepository
getRepository() {
271 return CmsContentRepository
.this;
279 public String
getNamespaceURI(String prefix
) {
280 return NamespaceUtils
.getNamespaceURI((p
) -> contentTypesManager
.getPrefixes().get(p
), prefix
);
284 public Iterator
<String
> getPrefixes(String namespaceURI
) {
285 return NamespaceUtils
.getPrefixes(
286 (ns
) -> contentTypesManager
.getPrefixes().entrySet().stream().filter(e
-> e
.getValue().equals(ns
))
287 .map(Map
.Entry
::getKey
).collect(Collectors
.toUnmodifiableSet()),
292 // public String findNamespace(String prefix) {
293 // return prefixes.get(prefix);
297 // public Set<String> findPrefixes(String namespaceURI) {
298 // Set<String> res = prefixes.entrySet().stream().filter(e -> e.getValue().equals(namespaceURI))
299 // .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet());
305 // public String findPrefix(String namespaceURI) {
306 // if (CrName.CR_NAMESPACE_URI.equals(namespaceURI) && prefixes.containsKey(CrName.CR_DEFAULT_PREFIX))
307 // return CrName.CR_DEFAULT_PREFIX;
308 // return ProvidedSession.super.findPrefix(namespaceURI);