]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java
50438e89a094b4cf7411c3bb86a7358eb93a5b30
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / CmsContentRepository.java
1 package org.argeo.cms.acr;
2
3 import java.io.IOException;
4 import java.io.Writer;
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;
12 import java.util.Map;
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;
18
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;
32
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;
54
55 /**
56 * Base implementation of a {@link ProvidedRepository} integrated with a CMS.
57 */
58 public class CmsContentRepository implements ProvidedRepository {
59 private final static CmsLog log = CmsLog.getLog(CmsContentRepository.class);
60
61 private NavigableMap<String, ContentProvider> partitions = new TreeMap<>();
62
63 // TODO synchronize ?
64 // private NavigableMap<String, String> prefixes = new TreeMap<>();
65
66 // private Schema schema;
67 private ContentTypesManager contentTypesManager;
68
69 private CmsContentSession systemSession;
70
71 private Map<CmsSession, CmsContentSession> userSessions = Collections.synchronizedMap(new HashMap<>());
72
73 public CmsContentRepository() {
74 contentTypesManager = new ContentTypesManager();
75 contentTypesManager.init();
76 contentTypesManager.listTypes();
77
78 systemSession = newSystemSession();
79 }
80
81 protected CmsContentSession newSystemSession() {
82 LoginContext loginContext;
83 try {
84 loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName());
85 loginContext.login();
86 } catch (LoginException e1) {
87 throw new RuntimeException("Could not login as data admin", e1);
88 } finally {
89 }
90 return new CmsContentSession(loginContext.getSubject(), Locale.getDefault());
91 }
92
93 public void start() {
94 }
95
96 public void stop() {
97
98 }
99
100 /*
101 * REPOSITORY
102 */
103
104 @Override
105 public ContentSession get() {
106 return get(CmsContextImpl.getCmsContext().getDefaultLocale());
107 }
108
109 @Override
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);
119 });
120 contentSession = newContentSession;
121 }
122 return contentSession;
123 }
124
125 public void addProvider(String base, ContentProvider provider) {
126 partitions.put(base, provider);
127 if ("/".equals(base))// root
128 return;
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");
136 }
137
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);
143 // return;
144 // }
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
148 }
149
150 /*
151 * FACTORIES
152 */
153 public void initRootContentProvider(Path path) {
154 try {
155 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
156 factory.setNamespaceAware(true);
157 factory.setXIncludeAware(true);
158 // factory.setSchema(schema);
159
160 factory.setSchema(contentTypesManager.getSchema());
161
162 DocumentBuilder dBuilder = factory.newDocumentBuilder();
163 dBuilder.setErrorHandler(new ErrorHandler() {
164
165 @Override
166 public void warning(SAXParseException exception) throws SAXException {
167 }
168
169 @Override
170 public void fatalError(SAXParseException exception) throws SAXException {
171 }
172
173 @Override
174 public void error(SAXParseException exception) throws SAXException {
175 log.error(exception);
176
177 }
178 });
179
180 Document document;
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);
186 // } else {
187 document = dBuilder.newDocument();
188 Element root = document.createElementNS(CrName.CR_NAMESPACE_URI, CrName.ROOT.get().toPrefixedString());
189
190 for (String prefix : contentTypesManager.getPrefixes().keySet()) {
191 root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix,
192 contentTypesManager.getPrefixes().get(prefix));
193 }
194
195 document.appendChild(root);
196
197 // write it
198 if (path != null) {
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);
205 }
206 }
207 // }
208
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);
214 }
215
216 }
217
218 /*
219 * NAMESPACE CONTEXT
220 */
221
222 /*
223 * SESSION
224 */
225
226 class CmsContentSession implements ProvidedSession {
227 private Subject subject;
228 private Locale locale;
229
230 private CompletableFuture<ProvidedSession> closed = new CompletableFuture<>();
231
232 public CmsContentSession(Subject subject, Locale locale) {
233 this.subject = subject;
234 this.locale = locale;
235 }
236
237 public void close() {
238 closed.complete(this);
239 }
240
241 @Override
242 public CompletionStage<ProvidedSession> onClose() {
243 return closed.minimalCompletionStage();
244 }
245
246 @Override
247 public Content get(String path) {
248 Map.Entry<String, ContentProvider> entry = partitions.floorEntry(path);
249 if (entry == null)
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);
257 }
258
259 @Override
260 public Subject getSubject() {
261 return subject;
262 }
263
264 @Override
265 public Locale getLocale() {
266 return locale;
267 }
268
269 @Override
270 public ProvidedRepository getRepository() {
271 return CmsContentRepository.this;
272 }
273
274 /*
275 * NAMESPACE CONTEXT
276 */
277
278 @Override
279 public String getNamespaceURI(String prefix) {
280 return NamespaceUtils.getNamespaceURI((p) -> contentTypesManager.getPrefixes().get(p), prefix);
281 }
282
283 @Override
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()),
288 namespaceURI);
289 }
290
291 // @Override
292 // public String findNamespace(String prefix) {
293 // return prefixes.get(prefix);
294 // }
295 //
296 // @Override
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());
300 //
301 // return res;
302 // }
303 //
304 // @Override
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);
309 // }
310
311 }
312
313 }