1 | ;;+----------------------------------------------------------------------------- |
---|
2 | ;;+ Isidorus |
---|
3 | ;;+ (c) 2008-2010 Marc Kuester, Christoph Ludwig, Lukas Georgieff |
---|
4 | ;;+ |
---|
5 | ;;+ Isidorus is freely distributable under the LLGPL license. |
---|
6 | ;;+ You can find a detailed description in trunk/docs/LLGPL-LICENSE.txt and |
---|
7 | ;;+ trunk/docs/LGPL-LICENSE.txt. |
---|
8 | ;;+----------------------------------------------------------------------------- |
---|
9 | |
---|
10 | (in-package :rest-interface) |
---|
11 | |
---|
12 | ;caching tables |
---|
13 | (defparameter *type-table* nil "Cointains integer==OIDs that represent a topic |
---|
14 | instance of a vylid type-topic") |
---|
15 | (defparameter *instance-table* nil "Cointains integer==OIDs that represent a topic |
---|
16 | instance of a valid instance-topic") |
---|
17 | |
---|
18 | (defparameter *cache-initialised* nil "determines wheter the cache has been |
---|
19 | already set or not") |
---|
20 | |
---|
21 | (defparameter *fragments-initialised* nil "determines wheter the fragments has |
---|
22 | been already initialised or not.") |
---|
23 | |
---|
24 | ;the prefix to get a fragment by the psi -> localhost:8000/json/get/<fragment-psi> |
---|
25 | (defparameter *json-get-prefix* "/json/get/(.+)$") |
---|
26 | ;the prefix to get a fragment by the psi -> localhost:8000/json/rdf/get/<fragment-psi> |
---|
27 | (defparameter *get-rdf-prefix* "/json/get/rdf/(.+)$") |
---|
28 | ;the url to commit a json fragment by "put" or "post" |
---|
29 | (defparameter *json-commit-url* "/json/commit/?$") |
---|
30 | ;the url to commit a TM-fragment in XTM 2.0 format, the regular |
---|
31 | ;expression represents the topic map id |
---|
32 | (defparameter *xtm-commit-prefix* "/import/xtm/2.0/(.+)$") |
---|
33 | ;the url to get all topic psis of isidorus -> localhost:8000/json/psis |
---|
34 | (defparameter *json-get-all-psis* "/json/psis/?$") |
---|
35 | ;the url to get a summary of all topic stored in isidorus; you have to set the |
---|
36 | ;GET-parameter "start" for the start index of all topics within elephant and the |
---|
37 | ;GET-paramter "end" for the last index of the topic sequence |
---|
38 | ; -> http://localhost:8000/json/summary/?start=12&end=13 |
---|
39 | (defparameter *json-get-summary-url* "/json/summary/?$") |
---|
40 | ;returns a list of all psis that can be a type |
---|
41 | (defparameter *json-get-all-type-psis* "/json/tmcl/types/?$") |
---|
42 | ;returns a list of all psis that belongs to a valid topic-instance |
---|
43 | (defparameter *json-get-all-instance-psis* "/json/tmcl/instances/?$") |
---|
44 | ;the json prefix for getting some topic stub information of a topic |
---|
45 | (defparameter *json-get-topic-stub-prefix* "/json/topicstubs/(.+)$") |
---|
46 | ;the json url for getting some tmcl information of a topic treated as a type |
---|
47 | (defparameter *json-get-type-tmcl-url* "/json/tmcl/type/?$") |
---|
48 | ;the json url for getting some tmcl information of a topic treated as an instance |
---|
49 | (defparameter *json-get-instance-tmcl-url* "/json/tmcl/instance/?$") |
---|
50 | ;returns a json-object representing a tree view |
---|
51 | (defparameter *json-get-overview* "/json/tmcl/overview/?$") |
---|
52 | ;the url to the user interface |
---|
53 | (defparameter *ajax-user-interface-url* "/isidorus") |
---|
54 | ;the url to the css files of the user interface |
---|
55 | (defparameter *ajax-user-interface-css-prefix* "/css") |
---|
56 | ;the directory contains the css files |
---|
57 | (defparameter *ajax-user-interface-css-directory-path* "ajax/css") |
---|
58 | ;the file path to the HTML file implements the user interface |
---|
59 | (defparameter *ajax-user-interface-file-path* "ajax/isidorus.html") |
---|
60 | ;the directory which contains all necessary javascript files |
---|
61 | (defparameter *ajax-javascript-directory-path* "ajax/javascripts") |
---|
62 | ;the url prefix of all javascript files |
---|
63 | (defparameter *ajax-javascript-url-prefix* "/javascripts") |
---|
64 | ;the url suffix that calls the mark-as-deleted handler |
---|
65 | (defparameter *mark-as-deleted-url* "/mark-as-deleted") |
---|
66 | ;the get url to request the latest revision of the storage |
---|
67 | (defparameter *latest-revision-url* "/json/latest-revision/?$") |
---|
68 | ;the ulr to invoke a SPARQL query |
---|
69 | (defparameter *sparql-url* "/json/tm-sparql/?$") |
---|
70 | |
---|
71 | |
---|
72 | (defun set-up-json-interface (&key (json-get-prefix *json-get-prefix*) |
---|
73 | (get-rdf-prefix *get-rdf-prefix*) |
---|
74 | (json-get-all-psis *json-get-all-psis*) |
---|
75 | (json-commit-url *json-commit-url*) |
---|
76 | (json-get-summary-url *json-get-summary-url*) |
---|
77 | (json-get-all-type-psis *json-get-all-type-psis*) |
---|
78 | (json-get-all-instance-psis *json-get-all-instance-psis*) |
---|
79 | (json-get-topic-stub-prefix *json-get-topic-stub-prefix*) |
---|
80 | (json-get-type-tmcl-url *json-get-type-tmcl-url*) |
---|
81 | (json-get-instance-tmcl-url *json-get-instance-tmcl-url*) |
---|
82 | (json-get-overview *json-get-overview*) |
---|
83 | (ajax-user-interface-url *ajax-user-interface-url*) |
---|
84 | (ajax-user-interface-file-path *ajax-user-interface-file-path*) |
---|
85 | (ajax-user-interface-css-prefix *ajax-user-interface-css-prefix*) |
---|
86 | (ajax-user-interface-css-directory-path *ajax-user-interface-css-directory-path*) |
---|
87 | (ajax-javascripts-directory-path *ajax-javascript-directory-path*) |
---|
88 | (ajax-javascripts-url-prefix *ajax-javascript-url-prefix*) |
---|
89 | (mark-as-deleted-url *mark-as-deleted-url*) |
---|
90 | (latest-revision-url *latest-revision-url*) |
---|
91 | (xtm-commit-prefix *xtm-commit-prefix*) |
---|
92 | (sparql-url *sparql-url*)) |
---|
93 | "registers the json im/exporter to the passed base-url in hunchentoot's dispatch-table |
---|
94 | and also registers a file-hanlder to the html-user-interface" |
---|
95 | |
---|
96 | ;initializes cache and fragments |
---|
97 | (init-cache nil) |
---|
98 | (format t "~%") |
---|
99 | (init-fragments nil) |
---|
100 | |
---|
101 | ;; registers the http-code 500 for an internal server error to the standard |
---|
102 | ;; return codes. so there won't be attached a hunchentoot default message, |
---|
103 | ;; this is necessary to be able to send error messages in an individual way/syntax |
---|
104 | ;; e.g. a json error-message. |
---|
105 | (push hunchentoot:+http-internal-server-error+ hunchentoot:*approved-return-codes*) |
---|
106 | ;; === html and css files ==================================================== |
---|
107 | (push |
---|
108 | (create-static-file-dispatcher-and-handler ajax-user-interface-url ajax-user-interface-file-path "text/html") |
---|
109 | hunchentoot:*dispatch-table*) |
---|
110 | |
---|
111 | (dolist (script-path-and-url (make-file-path-and-url ajax-user-interface-css-directory-path ajax-user-interface-css-prefix)) |
---|
112 | (let ((script-path (getf script-path-and-url :path)) |
---|
113 | (script-url (getf script-path-and-url :url))) |
---|
114 | (push |
---|
115 | (create-static-file-dispatcher-and-handler script-url script-path) |
---|
116 | hunchentoot:*dispatch-table*))) |
---|
117 | |
---|
118 | |
---|
119 | ;; === ajax frameworks and javascript files ================================== |
---|
120 | (dolist (script-path-and-url (make-file-path-and-url ajax-javascripts-directory-path ajax-javascripts-url-prefix)) |
---|
121 | (let ((script-path (getf script-path-and-url :path)) |
---|
122 | (script-url (getf script-path-and-url :url))) |
---|
123 | (push |
---|
124 | (create-static-file-dispatcher-and-handler script-url script-path) |
---|
125 | hunchentoot:*dispatch-table*))) |
---|
126 | |
---|
127 | |
---|
128 | ;; === rest interface ======================================================== |
---|
129 | (push |
---|
130 | (create-regex-dispatcher json-get-all-psis #'return-all-topic-psis) |
---|
131 | hunchentoot:*dispatch-table*) |
---|
132 | (push |
---|
133 | (create-regex-dispatcher json-get-prefix #'return-json-fragment) |
---|
134 | hunchentoot:*dispatch-table*) |
---|
135 | (push |
---|
136 | (create-regex-dispatcher get-rdf-prefix #'return-json-rdf-fragment) |
---|
137 | hunchentoot:*dispatch-table*) |
---|
138 | (push |
---|
139 | (create-regex-dispatcher json-get-topic-stub-prefix #'return-topic-stub-of-psi) |
---|
140 | hunchentoot:*dispatch-table*) |
---|
141 | (push |
---|
142 | (create-regex-dispatcher json-get-all-type-psis #'return-all-tmcl-types) |
---|
143 | hunchentoot:*dispatch-table*) |
---|
144 | (push |
---|
145 | (create-regex-dispatcher json-get-all-instance-psis #'return-all-tmcl-instances) |
---|
146 | hunchentoot:*dispatch-table*) |
---|
147 | (push |
---|
148 | (create-regex-dispatcher json-get-type-tmcl-url #'(lambda(&optional param) |
---|
149 | (declare (ignorable param)) |
---|
150 | (return-tmcl-info-of-psis 'json-tmcl::type))) |
---|
151 | hunchentoot:*dispatch-table*) |
---|
152 | (push |
---|
153 | (create-regex-dispatcher json-get-instance-tmcl-url #'(lambda(&optional param) |
---|
154 | (declare (ignorable param)) |
---|
155 | (return-tmcl-info-of-psis 'json-tmcl::instance))) |
---|
156 | hunchentoot:*dispatch-table*) |
---|
157 | (push |
---|
158 | (create-regex-dispatcher json-get-overview #'return-overview) |
---|
159 | hunchentoot:*dispatch-table*) |
---|
160 | (push |
---|
161 | (create-regex-dispatcher json-commit-url #'json-commit) |
---|
162 | hunchentoot:*dispatch-table*) |
---|
163 | (push |
---|
164 | (create-regex-dispatcher json-get-summary-url #'return-topic-summaries) |
---|
165 | hunchentoot:*dispatch-table*) |
---|
166 | (push |
---|
167 | (create-regex-dispatcher mark-as-deleted-url #'mark-as-deleted-handler) |
---|
168 | hunchentoot:*dispatch-table*) |
---|
169 | (push |
---|
170 | (create-regex-dispatcher xtm-commit-prefix #'xtm-import-handler) |
---|
171 | hunchentoot:*dispatch-table*) |
---|
172 | (push |
---|
173 | (create-regex-dispatcher latest-revision-url #'return-latest-revision) |
---|
174 | hunchentoot:*dispatch-table*) |
---|
175 | (push |
---|
176 | (create-regex-dispatcher sparql-url #'return-tm-sparql) |
---|
177 | hunchentoot:*dispatch-table*)) |
---|
178 | |
---|
179 | ;; ============================================================================= |
---|
180 | ;; --- some handlers for the json-rest-interface ------------------------------- |
---|
181 | ;; ============================================================================= |
---|
182 | (defun return-all-tmcl-types(&optional param) |
---|
183 | "Returns all topic-psi that are valid types -> so they have to be valid to the |
---|
184 | topictype-constraint (if it exists) and the can't be abstract." |
---|
185 | (declare (ignorable param)) |
---|
186 | (handler-case (let ((topic-types |
---|
187 | (with-reader-lock |
---|
188 | (map 'list #'(lambda (oid) |
---|
189 | (elephant::controller-recreate-instance |
---|
190 | elephant::*store-controller* oid)) |
---|
191 | *type-table*)))) |
---|
192 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
193 | (json:encode-json-to-string |
---|
194 | (map 'list #'(lambda(y) |
---|
195 | (map 'list #'uri y)) |
---|
196 | (map 'list #'psis topic-types)))) |
---|
197 | (condition (err) (progn |
---|
198 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
199 | (setf (hunchentoot:content-type*) "text") |
---|
200 | (format nil "Condition: \"~a\"" err))))) |
---|
201 | |
---|
202 | |
---|
203 | (defun return-all-tmcl-instances(&optional param) |
---|
204 | "Returns all topic-psis that are valid instances of any topic type. |
---|
205 | The validity is only oriented on the typing of topics, e.g. |
---|
206 | type-instance or supertype-subtype." |
---|
207 | (declare (ignorable param)) |
---|
208 | (handler-case (let ((topic-instances |
---|
209 | (with-reader-lock |
---|
210 | (map 'list #'(lambda (oid) |
---|
211 | (elephant::controller-recreate-instance |
---|
212 | elephant::*store-controller* oid)) |
---|
213 | *instance-table*)))) |
---|
214 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
215 | (json:encode-json-to-string |
---|
216 | (map 'list #'(lambda(y) |
---|
217 | (map 'list #'uri y)) |
---|
218 | (map 'list #'psis topic-instances)))) |
---|
219 | (condition (err) (progn |
---|
220 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
221 | (setf (hunchentoot:content-type*) "text") |
---|
222 | (format nil "Condition: \"~a\"" err))))) |
---|
223 | |
---|
224 | |
---|
225 | (defun return-topic-stub-of-psi(&optional psi) |
---|
226 | "Returns a json string of a topic depending on the |
---|
227 | passed psi as a topic-stub-construct." |
---|
228 | (assert psi) |
---|
229 | (let ((topic (d:get-item-by-psi psi))) |
---|
230 | (if topic |
---|
231 | (let ((topic-json |
---|
232 | (handler-case |
---|
233 | (with-reader-lock |
---|
234 | (json-exporter::to-json-topicStub-string topic :revision 0)) |
---|
235 | (condition (err) (progn |
---|
236 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
237 | (setf (hunchentoot:content-type*) "text") |
---|
238 | (format nil "Condition: \"~a\"" err)))))) |
---|
239 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
240 | topic-json) |
---|
241 | (progn |
---|
242 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
243 | (setf (hunchentoot:content-type*) "text") |
---|
244 | (format nil "Condition: Topic \"~a\" not found" psi))))) |
---|
245 | |
---|
246 | |
---|
247 | (defun return-tmcl-info-of-psis(treat-as) |
---|
248 | "Returns a json string which represents the defined tmcl-constraints of the |
---|
249 | topic and the associations where this topic can be a player." |
---|
250 | (let ((http-method (hunchentoot:request-method*))) |
---|
251 | (if (or (eq http-method :POST) |
---|
252 | (eq http-method :PUT)) |
---|
253 | (let ((external-format |
---|
254 | (flexi-streams:make-external-format :UTF-8 :eol-style :LF))) |
---|
255 | (let ((json-data |
---|
256 | (hunchentoot:raw-post-data :external-format external-format |
---|
257 | :force-text t))) |
---|
258 | (handler-case |
---|
259 | (let ((psis |
---|
260 | (json:decode-json-from-string json-data))) |
---|
261 | (let ((tmcl |
---|
262 | (with-reader-lock |
---|
263 | (json-tmcl:get-constraints-of-fragment |
---|
264 | psis :treat-as treat-as :revision 0)))) |
---|
265 | (if tmcl |
---|
266 | (progn |
---|
267 | (setf (hunchentoot:content-type*) |
---|
268 | "application/json") ;RFC 4627 |
---|
269 | tmcl) |
---|
270 | (progn |
---|
271 | (setf (hunchentoot:return-code*) |
---|
272 | hunchentoot:+http-not-found+) |
---|
273 | (setf (hunchentoot:content-type*) "text") |
---|
274 | (format nil "Topic \"~a\" not found." psis))))) |
---|
275 | (condition () |
---|
276 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
277 | "{\"topicConstraints\":{\"exclusiveInstances\":null,\"subjectIdentifierConstraints\":null,\"subjectLocatorConstraints\":null,\"topicNameConstraints\":null,\"topicOccurrenceConstraints\":null,\"abstractConstraint\":false},\"associationsConstraints\":null}")))) |
---|
278 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
279 | |
---|
280 | |
---|
281 | (defun return-all-topic-psis (&optional param) |
---|
282 | "return all psis currently existing in isidorus as a list of list. every topic is a list |
---|
283 | of psis and the entire list contains a list of topics" |
---|
284 | (declare (ignorable param)) |
---|
285 | (let ((http-method (hunchentoot:request-method*))) |
---|
286 | (if (eq http-method :GET) |
---|
287 | (progn |
---|
288 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
289 | (handler-case (with-reader-lock |
---|
290 | (get-all-topic-psis :revision 0)) |
---|
291 | (condition (err) (progn |
---|
292 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
293 | (setf (hunchentoot:content-type*) "text") |
---|
294 | (format nil "Condition: \"~a\"" err))))) |
---|
295 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
296 | |
---|
297 | |
---|
298 | (defun return-json-fragment(&optional psi) |
---|
299 | "returns the json-fragmen belonging to the psi passed by the parameter psi" |
---|
300 | (assert psi) |
---|
301 | (let ((http-method (hunchentoot:request-method*))) |
---|
302 | (if (eq http-method :GET) |
---|
303 | (let ((identifier (string-replace psi "%23" "#"))) |
---|
304 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
305 | (let ((fragment |
---|
306 | (with-reader-lock |
---|
307 | (get-latest-fragment-of-topic identifier)))) |
---|
308 | (if fragment |
---|
309 | (handler-case (with-reader-lock |
---|
310 | (export-construct-as-isidorus-json-string |
---|
311 | fragment :revision 0)) |
---|
312 | (condition (err) |
---|
313 | (progn |
---|
314 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
315 | (setf (hunchentoot:content-type*) "text") |
---|
316 | (format nil "Condition: \"~a\"" err)))) |
---|
317 | (progn |
---|
318 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
319 | (setf (hunchentoot:content-type*) "text") |
---|
320 | (format nil "Topic \"~a\" not found" psi))))) |
---|
321 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
322 | |
---|
323 | |
---|
324 | (defun return-json-rdf-fragment(&optional psi) |
---|
325 | "returns the json-fragmen belonging to the psi passed by the parameter psi" |
---|
326 | (assert psi) |
---|
327 | (let ((http-method (hunchentoot:request-method*))) |
---|
328 | (if (eq http-method :GET) |
---|
329 | (let ((identifier (string-replace psi "%23" "#"))) |
---|
330 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
331 | (let ((fragment |
---|
332 | (with-reader-lock |
---|
333 | (get-latest-fragment-of-topic identifier)))) |
---|
334 | (if fragment |
---|
335 | (handler-case (with-reader-lock |
---|
336 | (rdf-exporter:to-rdf-string fragment)) |
---|
337 | (condition (err) |
---|
338 | (progn |
---|
339 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
340 | (setf (hunchentoot:content-type*) "text") |
---|
341 | (format nil "Condition: \"~a\"" err)))) |
---|
342 | (progn |
---|
343 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
344 | (setf (hunchentoot:content-type*) "text") |
---|
345 | (format nil "Topic \"~a\" not found" psi))))) |
---|
346 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
347 | |
---|
348 | |
---|
349 | (defun json-commit(&optional param) |
---|
350 | "calls the import-from-isidorus-json method for a json-fragment and |
---|
351 | imports it to elephant" |
---|
352 | (declare (ignorable param)) ;param is currently not used |
---|
353 | (let ((http-method (hunchentoot:request-method*))) |
---|
354 | (if (or (eq http-method :PUT) |
---|
355 | (eq http-method :POST)) |
---|
356 | (let ((external-format (flexi-streams:make-external-format :UTF-8 :eol-style :LF))) |
---|
357 | (let ((json-data (hunchentoot:raw-post-data :external-format external-format :force-text t))) |
---|
358 | (handler-case |
---|
359 | (with-writer-lock |
---|
360 | (let ((frag (json-importer:import-from-isidorus-json json-data))) |
---|
361 | (when frag |
---|
362 | (push-to-cache (d:topic frag))))) |
---|
363 | (condition (err) |
---|
364 | (progn |
---|
365 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
366 | (setf (hunchentoot:content-type*) "text") |
---|
367 | (format nil "Condition: \"~a\"" err)))))) |
---|
368 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
369 | |
---|
370 | |
---|
371 | (defun return-topic-summaries(&optional param) |
---|
372 | "returns a summary of the requested topics" |
---|
373 | (declare (ignorable param)) |
---|
374 | (let ((start-idx |
---|
375 | (handler-case (parse-integer (hunchentoot:get-parameter "start")) |
---|
376 | (condition () 0))) |
---|
377 | (end-idx |
---|
378 | (handler-case (parse-integer (hunchentoot:get-parameter "end")) |
---|
379 | (condition () nil)))) |
---|
380 | (handler-case (with-reader-lock |
---|
381 | (let ((topics |
---|
382 | (elephant:get-instances-by-class 'd:TopicC))) |
---|
383 | (let ((end |
---|
384 | (cond |
---|
385 | ((not end-idx) |
---|
386 | (length topics)) |
---|
387 | ((> end-idx (length topics)) |
---|
388 | (length topics)) |
---|
389 | ((< end-idx 0) |
---|
390 | 0) |
---|
391 | (t |
---|
392 | end-idx)))) |
---|
393 | (let ((start |
---|
394 | (cond |
---|
395 | ((> start-idx (length topics)) |
---|
396 | end) |
---|
397 | ((< start-idx 0) |
---|
398 | 0) |
---|
399 | (t |
---|
400 | start-idx)))) |
---|
401 | (let ((topics-in-range |
---|
402 | (if (<= start end) |
---|
403 | (subseq topics start end) |
---|
404 | (reverse (subseq topics end start))))) |
---|
405 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
406 | (json-exporter:make-topic-summary topics-in-range)))))) |
---|
407 | (condition (err) (progn |
---|
408 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
409 | (setf (hunchentoot:content-type*) "text") |
---|
410 | (format nil "Condition: \"~a\"" err)))))) |
---|
411 | |
---|
412 | |
---|
413 | (defun return-overview (&optional param) |
---|
414 | "Returns a json-object representing a topic map overview as a tree(s)" |
---|
415 | (declare (ignorable param)) |
---|
416 | (with-reader-lock |
---|
417 | (handler-case |
---|
418 | (let ((json-string |
---|
419 | (json-tmcl::tree-view-to-json-string |
---|
420 | (json-tmcl::make-tree-view :revision 0)))) |
---|
421 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
422 | json-string) |
---|
423 | (Condition (err) |
---|
424 | (progn |
---|
425 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
426 | (setf (hunchentoot:content-type*) "text") |
---|
427 | (format nil "Condition: \"~a\"" err)))))) |
---|
428 | |
---|
429 | |
---|
430 | (defun mark-as-deleted-handler (&optional param) |
---|
431 | "Marks the corresponding elem as deleted." |
---|
432 | (declare (ignorable param)) ;param is currently not used |
---|
433 | (let ((http-method (hunchentoot:request-method*))) |
---|
434 | (if (or (eq http-method :DELETE) |
---|
435 | (eq http-method :POST)) ;not nice - but the current ui-library can't send http-delete messages |
---|
436 | (let ((external-format (flexi-streams:make-external-format :UTF-8 :eol-style :LF))) |
---|
437 | (let ((json-data (hunchentoot:raw-post-data :external-format external-format :force-text t))) |
---|
438 | (handler-case |
---|
439 | (with-writer-lock |
---|
440 | (let ((result (json-delete-interface:mark-as-deleted-from-json |
---|
441 | json-data :revision (d:get-revision)))) |
---|
442 | (if result |
---|
443 | (progn |
---|
444 | (when (typep result 'd:TopicC) |
---|
445 | (append ;;the append function is used only for suppress |
---|
446 | ;;style warnings of unused delete return values |
---|
447 | (delete (elephant::oid result) *type-table*) |
---|
448 | (delete (elephant::oid result) *instance-table*))) |
---|
449 | (format nil "")) ;operation succeeded |
---|
450 | (progn |
---|
451 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
452 | (format nil "object not found"))))) |
---|
453 | (condition (err) |
---|
454 | (progn |
---|
455 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
456 | (setf (hunchentoot:content-type*) "text") |
---|
457 | (format nil "Condition: \"~a\"" err)))))) |
---|
458 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
459 | |
---|
460 | |
---|
461 | (defun return-latest-revision () |
---|
462 | "Returns an integer that represents the latest revision that |
---|
463 | is used in the storage." |
---|
464 | (handler-case |
---|
465 | (if (eql (hunchentoot:request-method*) :GET) |
---|
466 | (let ((sorted-revisions |
---|
467 | (with-reader-lock (sort (d:get-all-revisions) #'>)))) |
---|
468 | (when sorted-revisions |
---|
469 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
470 | (format nil "~a" (first sorted-revisions)))) |
---|
471 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)) |
---|
472 | (condition (err) |
---|
473 | (progn |
---|
474 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
475 | (setf (hunchentoot:content-type*) "text") |
---|
476 | (format nil "Condition: \"~a\"" err))))) |
---|
477 | |
---|
478 | |
---|
479 | (defun xtm-import-handler (&optional tm-id) |
---|
480 | "Imports the received data as XTM 2.0 topic map." |
---|
481 | (assert tm-id) |
---|
482 | (handler-case |
---|
483 | (if (eql (hunchentoot:request-method*) :POST) |
---|
484 | (let ((external-format (flexi-streams:make-external-format |
---|
485 | :UTF-8 :eol-style :LF))) |
---|
486 | (let ((xml-data (hunchentoot:raw-post-data |
---|
487 | :external-format external-format |
---|
488 | :force-text t))) |
---|
489 | (let ((xml-dom |
---|
490 | (dom:document-element |
---|
491 | (cxml:parse xml-data (cxml-dom:make-dom-builder))))) |
---|
492 | (xtm-importer:importer xml-dom :tm-id tm-id |
---|
493 | :xtm-id (xtm-importer::get-uuid)) |
---|
494 | (format nil "")))) |
---|
495 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)) |
---|
496 | (condition (err) |
---|
497 | (progn |
---|
498 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
499 | (setf (hunchentoot:content-type*) "text") |
---|
500 | (format nil "Condition: \"~a\"" err))))) |
---|
501 | |
---|
502 | |
---|
503 | (defun return-tm-sparql (&optional param) |
---|
504 | "Returns a JSON object representing a SPARQL response." |
---|
505 | (declare (Ignorable param)) |
---|
506 | (handler-case |
---|
507 | (if (eql (hunchentoot:request-method*) :POST) |
---|
508 | (let ((external-format (flexi-streams:make-external-format |
---|
509 | :UTF-8 :eol-style :LF))) |
---|
510 | (let ((sparql-request (hunchentoot:raw-post-data |
---|
511 | :external-format external-format |
---|
512 | :force-text t))) |
---|
513 | (export-construct-as-isidorus-json-string |
---|
514 | (make-instance 'SPARQL-Query :query sparql-request |
---|
515 | :revision 0)))) |
---|
516 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)) |
---|
517 | (condition (err) |
---|
518 | (progn |
---|
519 | (setf (hunchentoot:return-code*) hunchentoot:+http-internal-server-error+) |
---|
520 | (setf (hunchentoot:content-type*) "text") |
---|
521 | (if (typep err 'SPARQL-Parser-Error) |
---|
522 | (format nil "SPARQL-Parser-Error: \"~a\"" (exceptions::message err)) |
---|
523 | (format nil "Condition: \"~a\"" err)))))) |
---|
524 | |
---|
525 | ;; ============================================================================= |
---|
526 | ;; --- some helper functions --------------------------------------------------- |
---|
527 | ;; ============================================================================= |
---|
528 | (defun make-file-path-and-url (path-to-files-directory url-prefix) |
---|
529 | "returns a list of lists which contains an absolute file path and a file-url |
---|
530 | concatenated of the url-prefix and the relative path of all all files in the |
---|
531 | passed directory and its subdirectories" |
---|
532 | (let ((start-position-of-relative-path |
---|
533 | (- (length (write-to-string (com.gigamonkeys.pathnames:file-exists-p |
---|
534 | path-to-files-directory))) 2))) |
---|
535 | (let ((files-and-urls nil)) |
---|
536 | (com.gigamonkeys.pathnames:walk-directory |
---|
537 | path-to-files-directory |
---|
538 | #'(lambda(current-path) |
---|
539 | (let ((current-path-string |
---|
540 | (write-to-string current-path))) |
---|
541 | (let ((last-position-of-current-path |
---|
542 | (- (length current-path-string) 1))) |
---|
543 | (let ((current-url |
---|
544 | (concat |
---|
545 | url-prefix |
---|
546 | (subseq current-path-string start-position-of-relative-path |
---|
547 | last-position-of-current-path)))) |
---|
548 | (push (list :path current-path :url current-url) files-and-urls)))))) |
---|
549 | files-and-urls))) |
---|
550 | |
---|
551 | |
---|
552 | (defun init-cache(force-init) |
---|
553 | "Initializes the type and instance cache-tables with all valid types/instances" |
---|
554 | (declare (Boolean force-init)) |
---|
555 | (when (or force-init (not *cache-initialised*)) |
---|
556 | (with-writer-lock |
---|
557 | (setf *type-table* nil) |
---|
558 | (setf *instance-table* nil) |
---|
559 | (let ((topictype (get-item-by-psi json-tmcl-constants::*topictype-psi* |
---|
560 | :revision 0)) |
---|
561 | (topictype-constraint (json-tmcl::is-type-constrained :revision 0))) |
---|
562 | (format t "~%initializing cache: ") |
---|
563 | (map 'list #'(lambda(top) |
---|
564 | (format t ".") |
---|
565 | (push-to-cache top topictype topictype-constraint)) |
---|
566 | (elephant:get-instances-by-class 'TopicC)))) |
---|
567 | (setf *cache-initialised* t))) |
---|
568 | |
---|
569 | |
---|
570 | (defun push-to-cache (topic-instance &optional |
---|
571 | (topictype |
---|
572 | (get-item-by-psi |
---|
573 | json-tmcl::*topictype-psi* :revision 0)) |
---|
574 | (topictype-constraint |
---|
575 | (json-tmcl::is-type-constrained :revision 0))) |
---|
576 | "Pushes the given topic-instance into the correspondng cache-tables" |
---|
577 | (when (not (json-tmcl::abstract-p topic-instance :revision 0)) |
---|
578 | (handler-case (progn |
---|
579 | (json-tmcl::topictype-p |
---|
580 | topic-instance topictype topictype-constraint nil 0) |
---|
581 | (pushnew (elephant::oid topic-instance) *type-table*)) |
---|
582 | (condition () nil))) |
---|
583 | (handler-case (progn |
---|
584 | (json-tmcl::valid-instance-p topic-instance nil nil 0) |
---|
585 | (pushnew (elephant::oid topic-instance) *instance-table*)) |
---|
586 | (condition () nil))) |
---|
587 | |
---|
588 | |
---|
589 | (defun init-fragments (force-init) |
---|
590 | "Creates fragments of all topics that have a PSI." |
---|
591 | (declare (Boolean force-init)) |
---|
592 | (when (or force-init (not *fragments-initialised*)) |
---|
593 | (format t "creating fragments: ") |
---|
594 | (map 'list #'(lambda(top) |
---|
595 | (let ((psis-of-top (psis top))) |
---|
596 | (when psis-of-top |
---|
597 | (format t ".") |
---|
598 | (create-latest-fragment-of-topic (uri (first psis-of-top)))))) |
---|
599 | (elephant:get-instances-by-class 'd:TopicC)) |
---|
600 | (setf *fragments-initialised* t))) |
---|