Mercurial > dive4elements > framework
comparison artifact-database/src/main/java/org/dive4elements/artifactdatabase/ArtifactDatabaseImpl.java @ 473:d0ac790a6c89 dive4elements-move
Moved directories to org.dive4elements
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 10:57:18 +0200 |
parents | artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java@8d8aed23c323 |
children | 415df0fc4fa1 |
comparison
equal
deleted
inserted
replaced
472:783cc1b6b615 | 473:d0ac790a6c89 |
---|---|
1 /* | |
2 * Copyright (c) 2010 by Intevation GmbH | |
3 * | |
4 * This program is free software under the LGPL (>=v2.1) | |
5 * Read the file LGPL.txt coming with the software for details | |
6 * or visit http://www.gnu.org/licenses/ if it does not exist. | |
7 */ | |
8 | |
9 package de.intevation.artifactdatabase; | |
10 | |
11 import de.intevation.artifacts.common.utils.XMLUtils; | |
12 import de.intevation.artifacts.common.utils.StringUtils; | |
13 | |
14 import de.intevation.artifactdatabase.Backend.PersistentArtifact; | |
15 | |
16 import de.intevation.artifacts.Artifact; | |
17 import de.intevation.artifacts.ArtifactCollection; | |
18 import de.intevation.artifacts.ArtifactCollectionFactory; | |
19 import de.intevation.artifacts.ArtifactDatabase; | |
20 import de.intevation.artifacts.ArtifactDatabaseException; | |
21 import de.intevation.artifacts.ArtifactFactory; | |
22 import de.intevation.artifacts.ArtifactNamespaceContext; | |
23 import de.intevation.artifacts.ArtifactSerializer; | |
24 import de.intevation.artifacts.CallContext; | |
25 import de.intevation.artifacts.CallMeta; | |
26 import de.intevation.artifacts.CollectionItem; | |
27 import de.intevation.artifacts.GlobalContext; | |
28 import de.intevation.artifacts.Hook; | |
29 import de.intevation.artifacts.Message; | |
30 import de.intevation.artifacts.Service; | |
31 import de.intevation.artifacts.ServiceFactory; | |
32 import de.intevation.artifacts.User; | |
33 import de.intevation.artifacts.UserFactory; | |
34 | |
35 import java.io.IOException; | |
36 import java.io.OutputStream; | |
37 | |
38 import java.security.MessageDigest; | |
39 import java.security.NoSuchAlgorithmException; | |
40 | |
41 import java.util.Arrays; | |
42 import java.util.Date; | |
43 import java.util.HashMap; | |
44 import java.util.HashSet; | |
45 import java.util.LinkedList; | |
46 import java.util.List; | |
47 import java.util.Map; | |
48 import java.util.Set; | |
49 | |
50 import javax.xml.xpath.XPathConstants; | |
51 | |
52 import org.apache.commons.codec.binary.Base64; | |
53 import org.apache.commons.codec.binary.Hex; | |
54 | |
55 import org.apache.log4j.Logger; | |
56 | |
57 import org.w3c.dom.Document; | |
58 import org.w3c.dom.Element; | |
59 import org.w3c.dom.Node; | |
60 | |
61 /** | |
62 * The core implementation of artifact database. This layer exposes | |
63 * the needed methods to the artifact runtime system which e.g. may | |
64 * expose them via REST. The concrete persistent representation of the | |
65 * artifacts is handled by the {@link Backend backend}. | |
66 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
67 */ | |
68 public class ArtifactDatabaseImpl | |
69 implements ArtifactDatabase, | |
70 DatabaseCleaner.LockedIdsProvider, | |
71 Backend.FactoryLookup | |
72 { | |
73 private static Logger logger = | |
74 Logger.getLogger(ArtifactDatabaseImpl.class); | |
75 | |
76 /** The key under which the artifact database is stored in the global | |
77 * context.*/ | |
78 public static final String GLOBAL_CONTEXT_KEY = "global.artifact.database"; | |
79 | |
80 /** Message that is returned if an operation was successful.*/ | |
81 public static final String OPERATION_SUCCESSFUL = | |
82 "SUCCESS"; | |
83 | |
84 /** Message that is returned if an operation failed.*/ | |
85 public static final String OPERATION_FAILURE = | |
86 "FAILURE"; | |
87 | |
88 /** | |
89 * Error message issued if a requested artifact factory | |
90 * is not registered to this database. | |
91 */ | |
92 public static final String NO_SUCH_FACTORY = | |
93 "No such factory"; | |
94 | |
95 /** | |
96 * Error message issued if a requested artifact is not found | |
97 * in this database. | |
98 */ | |
99 public static final String NO_SUCH_ARTIFACT = | |
100 "No such artifact"; | |
101 | |
102 /** | |
103 * Error message issued if a requested artifact is not found | |
104 * in this database. | |
105 */ | |
106 public static final String NO_SUCH_COLLECTION = | |
107 "No such collection"; | |
108 | |
109 /** | |
110 * Error message issued if the creation of an artifact failed. | |
111 */ | |
112 public static final String CREATION_FAILED = | |
113 "Creation of artifact failed"; | |
114 | |
115 /** | |
116 * Error message if an severe internal error occurred. | |
117 */ | |
118 public static final String INTERNAL_ERROR = | |
119 "Creation of artifact failed"; | |
120 | |
121 /** | |
122 * Error message issued if a requested service is not | |
123 * offered by this database. | |
124 */ | |
125 public static final String NO_SUCH_SERVICE = | |
126 "No such service"; | |
127 | |
128 /** | |
129 * Default digest hash to be used while im-/exporting artifacts. | |
130 */ | |
131 public static final String DIGEST_ALGORITHM = | |
132 "SHA-1"; | |
133 | |
134 /** | |
135 * XPath to get the checksum from an XML representation of | |
136 * an exported artifact. | |
137 */ | |
138 public static final String XPATH_IMPORT_CHECKSUM = | |
139 "/art:action/art:data/@checksum"; | |
140 | |
141 /** | |
142 * XPath to get the name of the factory which should be | |
143 * used to revive an antrifact that is going to be imported. | |
144 */ | |
145 public static final String XPATH_IMPORT_FACTORY = | |
146 "/art:action/art:data/@factory"; | |
147 | |
148 /** | |
149 * XPath to get the base64 encoded data of an artifact | |
150 * that is going to be imported. | |
151 */ | |
152 public static final String XPATH_IMPORT_DATA = | |
153 "/art:action/art:data/text()"; | |
154 | |
155 /** | |
156 * Error message issued if the checksum of an | |
157 * artifact to be imported has an invalid syntax. | |
158 */ | |
159 public static final String INVALID_CHECKSUM = | |
160 "Invalid checksum"; | |
161 | |
162 /** | |
163 * Error message issued the checksum validation | |
164 * of an artifact to be imported fails. | |
165 */ | |
166 public static final String CHECKSUM_MISMATCH = | |
167 "Mismatching checksum"; | |
168 | |
169 /** | |
170 * Error message issued if an artifact to be imported | |
171 * does not have any data. | |
172 */ | |
173 public static final String NO_DATA = | |
174 "No data"; | |
175 | |
176 /** | |
177 * Error message issued if the deserialization of | |
178 * an artifact to be imported fails. | |
179 */ | |
180 public static final String INVALID_ARTIFACT = | |
181 "Invalid artifact"; | |
182 | |
183 | |
184 // User constants | |
185 | |
186 /** | |
187 * Error message issued if the creation of a user failed. | |
188 */ | |
189 public static final String USER_CREATION_FAILED = | |
190 "Creation of user failed."; | |
191 | |
192 /** XPath to figure out the name of a new user.*/ | |
193 public static final String XPATH_USERNAME = | |
194 "/art:action/art:user/@name"; | |
195 | |
196 /** XPath to figure out the role of a new user.*/ | |
197 public static final String XPATH_USERROLE = | |
198 "/art:action/art:user/art:role"; | |
199 | |
200 /** XPath to figure out the account of a new user.*/ | |
201 public static final String XPATH_USERACCOUNT = | |
202 "/art:action/art:user/art:account/@name"; | |
203 | |
204 /** XPath to figure out the account of when searching for a user .*/ | |
205 public static final String XPATH_USERACCOUNT_FIND = | |
206 "/art:action/art:account/@name"; | |
207 | |
208 /** Error message if a specified user does not exist.*/ | |
209 public static final String NO_SUCH_USER = | |
210 "No such user"; | |
211 | |
212 /** Error message if no username is given for user creation.*/ | |
213 public static final String NO_USERNAME = | |
214 "Invalid username"; | |
215 | |
216 /** Error message if no user account is given for user creation.*/ | |
217 public static final String NO_USERACCOUNT = | |
218 "Invalid user account name"; | |
219 | |
220 // Collection constants | |
221 | |
222 /** | |
223 * Error message issued if the creation of a collection failed. | |
224 */ | |
225 public static final String COLLECTION_CREATION_FAILED = | |
226 "Creation of collection failed"; | |
227 | |
228 /** | |
229 * XPath to figure out the name of a collection described in the incoming | |
230 * document. | |
231 */ | |
232 public static final String XPATH_COLLECTION_NAME = | |
233 "/art:action/art:type/art:collection/@name"; | |
234 | |
235 /** | |
236 * XPath to figure out the attributes for a collection. | |
237 */ | |
238 public static final String XPATH_COLLECTION_ATTRIBUTE = | |
239 "/art:action/art:type/art:collection/art:attribute"; | |
240 | |
241 /** | |
242 * XPath to figure out the attributes for an artifact that is put into a | |
243 * collection. | |
244 */ | |
245 public static final String XPATH_COLLECTION_ITEM_ATTRIBUTE = | |
246 "/art:action/art:type/art:artifact/art:attribute"; | |
247 | |
248 /** | |
249 * XPath to figure out the time to live value for setting a new TTL. | |
250 */ | |
251 public static final String XPATH_COLLECTION_TTL = | |
252 "/art:action/art:type/art:ttl/@value"; | |
253 | |
254 | |
255 /** | |
256 * This inner class allows the deferral of writing the output | |
257 * of the artifact's out() call. | |
258 */ | |
259 public class DeferredOutputImpl | |
260 implements DeferredOutput | |
261 { | |
262 /** | |
263 * The persistence wrapper around a living artifact. | |
264 */ | |
265 protected PersistentArtifact artifact; | |
266 /** | |
267 * The output type. | |
268 */ | |
269 protected String type; | |
270 /** | |
271 * The input document for the artifact's out() call. | |
272 */ | |
273 protected Document format; | |
274 /** | |
275 * The meta information of the artifact's out() call. | |
276 */ | |
277 protected CallMeta callMeta; | |
278 | |
279 /** | |
280 * Default constructor. | |
281 */ | |
282 public DeferredOutputImpl() { | |
283 } | |
284 | |
285 /** | |
286 * Constructor to create a deferred execution unit for | |
287 * the artifact's out() call given an artifact, an input document | |
288 * an the meta information. | |
289 * @param artifact The persistence wrapper around a living artifact. | |
290 * @param format The input document for the artifact's out() call. | |
291 * @param callMeta The meta information of the artifact's out() call. | |
292 */ | |
293 public DeferredOutputImpl( | |
294 PersistentArtifact artifact, | |
295 String type, | |
296 Document format, | |
297 CallMeta callMeta | |
298 ) { | |
299 this.artifact = artifact; | |
300 this.type = type; | |
301 this.format = format; | |
302 this.callMeta = callMeta; | |
303 } | |
304 | |
305 public void write(OutputStream output) throws IOException { | |
306 | |
307 ArtifactCallContext cc = new ArtifactCallContext( | |
308 ArtifactDatabaseImpl.this, | |
309 CallContext.TOUCH, | |
310 callMeta, | |
311 artifact); | |
312 | |
313 try { | |
314 artifact.getArtifact().out(type, format, output, cc); | |
315 } | |
316 finally { | |
317 cc.postCall(); | |
318 } | |
319 } | |
320 } // class DeferredOutputImpl | |
321 | |
322 | |
323 /** | |
324 * This inner class allows the deferral of writing the output | |
325 * of the artifact's out() call. | |
326 */ | |
327 public class DeferredCollectionOutputImpl | |
328 implements DeferredOutput | |
329 { | |
330 /** | |
331 * The persistence wrapper around a living collection. | |
332 */ | |
333 protected ArtifactCollection collection; | |
334 /** | |
335 * The output type. | |
336 */ | |
337 protected String type; | |
338 /** | |
339 * The input document for the collection's out() call. | |
340 */ | |
341 protected Document format; | |
342 /** | |
343 * The meta information of the collection's out() call. | |
344 */ | |
345 protected CallMeta callMeta; | |
346 | |
347 /** | |
348 * Default constructor. | |
349 */ | |
350 public DeferredCollectionOutputImpl() { | |
351 } | |
352 | |
353 /** | |
354 * Constructor to create a deferred execution unit for | |
355 * the collection's out() call given a collection, an input document | |
356 * an the meta information. | |
357 * @param collection The collection. | |
358 * @param format The input document for the collection's out() call. | |
359 * @param callMeta The meta information of the collection's out() call. | |
360 */ | |
361 public DeferredCollectionOutputImpl( | |
362 ArtifactCollection collection, | |
363 String type, | |
364 Document format, | |
365 CallMeta callMeta | |
366 ) { | |
367 this.collection = collection; | |
368 this.type = type; | |
369 this.format = format; | |
370 this.callMeta = callMeta; | |
371 } | |
372 | |
373 public void write(OutputStream output) throws IOException { | |
374 | |
375 CollectionCallContext cc = new CollectionCallContext( | |
376 ArtifactDatabaseImpl.this, | |
377 CallContext.TOUCH, | |
378 callMeta, | |
379 collection); | |
380 | |
381 try { | |
382 collection.out(type, format, output, cc); | |
383 } | |
384 finally { | |
385 cc.postCall(); | |
386 } | |
387 } | |
388 } // class DeferredCollectionOutputImpl | |
389 | |
390 /** | |
391 * List of name/description pairs needed for | |
392 * {@link #artifactFactoryNamesAndDescriptions() }. | |
393 */ | |
394 protected String [][] factoryNamesAndDescription; | |
395 /** | |
396 * Map to access artifact factories by there name. | |
397 */ | |
398 protected HashMap name2factory; | |
399 | |
400 /** | |
401 * List of name/description pairs needed for | |
402 * {@link #serviceNamesAndDescriptions() }. | |
403 */ | |
404 protected String [][] serviceNamesAndDescription; | |
405 /** | |
406 * Map to access services by there name. | |
407 */ | |
408 protected HashMap name2service; | |
409 | |
410 /** | |
411 * The factory that is used to create new artifact collections. | |
412 */ | |
413 protected ArtifactCollectionFactory collectionFactory; | |
414 | |
415 /** | |
416 * The factory that is used to create and list users. | |
417 */ | |
418 protected UserFactory userFactory; | |
419 | |
420 /** | |
421 * Reference to the storage backend. | |
422 */ | |
423 protected Backend backend; | |
424 /** | |
425 * Reference of the global context of the artifact runtime system. | |
426 */ | |
427 protected GlobalContext context; | |
428 | |
429 /** | |
430 * The signing secret to be used for ex-/importing artifacts. | |
431 */ | |
432 protected byte [] exportSecret; | |
433 | |
434 /** | |
435 * A set of ids of artifact which currently running in background. | |
436 * This artifacts should not be removed from the database by the | |
437 * database cleaner. | |
438 */ | |
439 protected HashSet<Integer> backgroundIds; | |
440 | |
441 /** | |
442 * A list of background messages for Artifacts and Collections. | |
443 */ | |
444 protected Map<String, LinkedList<Message>> backgroundMsgs; | |
445 | |
446 | |
447 protected CallContext.Listener callContextListener; | |
448 | |
449 /** | |
450 * Hooks that are executed after an artifact has been fed. | |
451 */ | |
452 protected List<Hook> postFeedHooks; | |
453 | |
454 /** | |
455 * Hooks that are executed after an artifact has advanced. | |
456 */ | |
457 protected List<Hook> postAdvanceHooks; | |
458 | |
459 /** | |
460 * Hooks that are executed after an artifact's describe() operation was | |
461 * called. | |
462 */ | |
463 protected List<Hook> postDescribeHooks; | |
464 | |
465 protected List<LifetimeListener> lifetimeListeners; | |
466 | |
467 /** | |
468 * Default constructor. | |
469 */ | |
470 public ArtifactDatabaseImpl() { | |
471 } | |
472 | |
473 /** | |
474 * Constructor to create a artifact database with the given | |
475 * bootstrap parameters like artifact- and service factories et. al. | |
476 * Created this way the artifact database has no backend. | |
477 * @param bootstrap The parameters to start this artifact database. | |
478 */ | |
479 public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { | |
480 this(bootstrap, null); | |
481 } | |
482 | |
483 /** | |
484 * Constructor to create a artifact database with the a given | |
485 * backend and | |
486 * bootstrap parameters like artifact- and service factories et. al. | |
487 * @param bootstrap The parameters to start this artifact database. | |
488 * @param backend The storage backend. | |
489 */ | |
490 public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { | |
491 | |
492 logger.debug("new ArtifactDatabaseImpl"); | |
493 | |
494 backgroundIds = new HashSet<Integer>(); | |
495 backgroundMsgs = new HashMap<String, LinkedList<Message>>(); | |
496 | |
497 setupArtifactCollectionFactory(bootstrap); | |
498 setupArtifactFactories(bootstrap); | |
499 setupServices(bootstrap); | |
500 setupUserFactory(bootstrap); | |
501 setupCallContextListener(bootstrap); | |
502 setupHooks(bootstrap); | |
503 setupLifetimeListeners(bootstrap); | |
504 | |
505 context = bootstrap.getContext(); | |
506 context.put(GLOBAL_CONTEXT_KEY, this); | |
507 | |
508 exportSecret = bootstrap.getExportSecret(); | |
509 | |
510 wireWithBackend(backend, bootstrap); | |
511 } | |
512 | |
513 public CallContext.Listener getCallContextListener() { | |
514 return callContextListener; | |
515 } | |
516 | |
517 public void setCallContextListener( | |
518 CallContext.Listener callContextListener | |
519 ) { | |
520 this.callContextListener = callContextListener; | |
521 } | |
522 | |
523 | |
524 public void setPostFeedHook(List<Hook> postFeedHooks) { | |
525 this.postFeedHooks = postFeedHooks; | |
526 } | |
527 | |
528 public void setPostAdvanceHook(List<Hook> postAdvanceHooks) { | |
529 this.postAdvanceHooks = postAdvanceHooks; | |
530 } | |
531 | |
532 public void setPostDescribeHook(List<Hook> postDescribeHooks) { | |
533 this.postDescribeHooks = postDescribeHooks; | |
534 } | |
535 | |
536 /** | |
537 * Used to extract the artifact collection factory from bootstrap. | |
538 * | |
539 * @param bootstrap The bootstrap parameters. | |
540 */ | |
541 protected void setupArtifactCollectionFactory(FactoryBootstrap bootstrap) { | |
542 collectionFactory = bootstrap.getArtifactCollectionFactory(); | |
543 } | |
544 | |
545 /** | |
546 * Used to extract the artifact factories from the bootstrap | |
547 * parameters and building the internal lookup tables. | |
548 * @param bootstrap The bootstrap parameters. | |
549 */ | |
550 protected void setupArtifactFactories(FactoryBootstrap bootstrap) { | |
551 name2factory = new HashMap(); | |
552 | |
553 ArtifactFactory [] factories = bootstrap.getArtifactFactories(); | |
554 factoryNamesAndDescription = new String[factories.length][]; | |
555 | |
556 for (int i = 0; i < factories.length; ++i) { | |
557 | |
558 ArtifactFactory factory = factories[i]; | |
559 | |
560 String name = factory.getName(); | |
561 String description = factory.getDescription(); | |
562 | |
563 factoryNamesAndDescription[i] = | |
564 new String [] { name, description }; | |
565 | |
566 name2factory.put(name, factory); | |
567 } | |
568 } | |
569 | |
570 /** | |
571 * Used to extract the callContextListener from the bootstrap. | |
572 * | |
573 * @param bootstrap The bootstrap parameters. | |
574 */ | |
575 protected void setupCallContextListener(FactoryBootstrap bootstrap) { | |
576 setCallContextListener(bootstrap.getCallContextListener()); | |
577 } | |
578 | |
579 | |
580 protected void setupHooks(FactoryBootstrap bootstrap) { | |
581 setPostFeedHook(bootstrap.getPostFeedHooks()); | |
582 setPostAdvanceHook(bootstrap.getPostAdvanceHooks()); | |
583 setPostDescribeHook(bootstrap.getPostDescribeHooks()); | |
584 } | |
585 | |
586 protected void setupBackendListeners(FactoryBootstrap bootstrap) { | |
587 logger.debug("setupBackendListeners"); | |
588 List<BackendListener> bls = bootstrap.getBackendListeners(); | |
589 if (bls != null && !bls.isEmpty()) { | |
590 for (BackendListener listener: bls) { | |
591 listener.setup(context); | |
592 } | |
593 backend.addAllListeners(bls); | |
594 } | |
595 } | |
596 | |
597 protected void setupLifetimeListeners(FactoryBootstrap bootstrap) { | |
598 this.lifetimeListeners = bootstrap.getLifetimeListeners(); | |
599 } | |
600 | |
601 /** | |
602 * Used to extract the user factory from the bootstrap. | |
603 */ | |
604 protected void setupUserFactory(FactoryBootstrap bootstrap) { | |
605 userFactory = bootstrap.getUserFactory(); | |
606 } | |
607 | |
608 /** | |
609 * Used to extract the service factories from the bootstrap | |
610 * parameters, setting up the services and building the internal | |
611 * lookup tables. | |
612 * @param bootstrap The bootstrap parameters. | |
613 */ | |
614 protected void setupServices(FactoryBootstrap bootstrap) { | |
615 | |
616 name2service = new HashMap(); | |
617 | |
618 ServiceFactory [] serviceFactories = | |
619 bootstrap.getServiceFactories(); | |
620 | |
621 serviceNamesAndDescription = | |
622 new String[serviceFactories.length][]; | |
623 | |
624 for (int i = 0; i < serviceFactories.length; ++i) { | |
625 ServiceFactory factory = serviceFactories[i]; | |
626 | |
627 String name = factory.getName(); | |
628 String description = factory.getDescription(); | |
629 | |
630 serviceNamesAndDescription[i] = | |
631 new String [] { name, description }; | |
632 | |
633 name2service.put( | |
634 name, | |
635 factory.createService(bootstrap.getContext())); | |
636 } | |
637 | |
638 } | |
639 | |
640 /** | |
641 * Wires a storage backend to this artifact database and | |
642 * establishes a callback to be able to revive artifacts | |
643 * via the serializers of this artifact factories. | |
644 * @param backend The backend to be wired with this artifact database. | |
645 */ | |
646 public void wireWithBackend(Backend backend, FactoryBootstrap bootstrap) { | |
647 logger.debug("wireWithBackend"); | |
648 if (backend != null) { | |
649 this.backend = backend; | |
650 backend.setFactoryLookup(this); | |
651 setupBackendListeners(bootstrap); | |
652 } | |
653 } | |
654 | |
655 /** | |
656 * Called after an backgrounded artifact signals its | |
657 * will to be written back to the backend. | |
658 * @param artifact The persistence wrapper around | |
659 * the backgrounded artifact. | |
660 * @param action The action to be performed. | |
661 */ | |
662 protected void fromBackground(PersistentArtifact artifact, int action) { | |
663 logger.warn("BACKGROUND processing is not fully implemented, yet!"); | |
664 switch (action) { | |
665 case CallContext.NOTHING: | |
666 break; | |
667 case CallContext.TOUCH: | |
668 artifact.touch(); | |
669 break; | |
670 case CallContext.STORE: | |
671 artifact.store(); | |
672 break; | |
673 default: | |
674 logger.warn("operation not allowed in fromBackground"); | |
675 } | |
676 removeIdFromBackground(artifact.getId()); | |
677 removeBackgroundMessages(artifact.getArtifact().identifier()); | |
678 } | |
679 | |
680 /** | |
681 * Removes an artifact's database id from the set of backgrounded | |
682 * artifacts. The database cleaner is now able to remove it safely | |
683 * from the database again. | |
684 * @param id The database id of the artifact. | |
685 */ | |
686 protected void removeIdFromBackground(int id) { | |
687 synchronized (backgroundIds) { | |
688 backgroundIds.remove(id); | |
689 } | |
690 } | |
691 | |
692 | |
693 /** | |
694 * Removes all messages that have been added to the <i>backgroundMsgs</i> | |
695 * list. | |
696 * | |
697 * @param uuid The UUID of an artifact or collection. | |
698 */ | |
699 protected void removeBackgroundMessages(String uuid) { | |
700 logger.debug("Remove background messages for: " + uuid); | |
701 | |
702 synchronized (backgroundMsgs) { | |
703 backgroundMsgs.remove(uuid); | |
704 } | |
705 } | |
706 | |
707 /** | |
708 * Adds an artifact's database id to the set of artifacts | |
709 * running in backgroound. To be in this set prevents the | |
710 * artifact to be removed from the database by the database cleaner. | |
711 * @param id The database id of the artifact to be protected | |
712 * from being removed from the database. | |
713 */ | |
714 protected void addIdToBackground(int id) { | |
715 synchronized (backgroundIds) { | |
716 backgroundIds.add(Integer.valueOf(id)); | |
717 } | |
718 } | |
719 | |
720 /** | |
721 * Adds a <i>Message</i> to the background messages list of the Artifact or | |
722 * Collection. | |
723 * | |
724 * @param uuid The UUID of the Artifact or Collection. | |
725 * @param msg The message that should be added to the background messages | |
726 * list. | |
727 */ | |
728 public void addBackgroundMessage(String uuid, Message msg) { | |
729 logger.debug("Add new background messsage for: " + uuid); | |
730 | |
731 synchronized (backgroundMsgs) { | |
732 LinkedList<Message> messages = backgroundMsgs.get(uuid); | |
733 | |
734 if (messages == null) { | |
735 messages = new LinkedList<Message>(); | |
736 backgroundMsgs.put(uuid, messages); | |
737 } | |
738 | |
739 messages.addLast(msg); | |
740 } | |
741 } | |
742 | |
743 public Set<Integer> getLockedIds() { | |
744 synchronized (backgroundIds) { | |
745 return new HashSet<Integer>(backgroundIds); | |
746 } | |
747 } | |
748 | |
749 /** | |
750 * Returns the background <i>Message</i>s for a specific Artifact or | |
751 * Collection. | |
752 * | |
753 * @param uuid The Artifact's or Collection's UUID. | |
754 * | |
755 * @return a <i>List</i> of <i>Message</i>s or null if no messages are | |
756 * existing. | |
757 */ | |
758 public LinkedList<Message> getBackgroundMessages(String uuid) { | |
759 logger.debug("Retrieve background message for: " + uuid); | |
760 | |
761 synchronized (backgroundMsgs) { | |
762 return backgroundMsgs.get(uuid); | |
763 } | |
764 } | |
765 | |
766 public String [][] artifactFactoryNamesAndDescriptions() { | |
767 return factoryNamesAndDescription; | |
768 } | |
769 | |
770 public ArtifactFactory getInternalArtifactFactory(String factoryName) { | |
771 return getArtifactFactory(factoryName); | |
772 } | |
773 | |
774 public ArtifactFactory getArtifactFactory(String factoryName) { | |
775 return (ArtifactFactory)name2factory.get(factoryName); | |
776 } | |
777 | |
778 public UserFactory getUserFactory() { | |
779 return userFactory; | |
780 } | |
781 | |
782 public ArtifactCollectionFactory getArtifactCollectionFactory() { | |
783 return collectionFactory; | |
784 } | |
785 | |
786 public Document createArtifactWithFactory( | |
787 String factoryName, | |
788 CallMeta callMeta, | |
789 Document data | |
790 ) | |
791 throws ArtifactDatabaseException | |
792 { | |
793 logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory " | |
794 + factoryName); | |
795 ArtifactFactory factory = getArtifactFactory(factoryName); | |
796 | |
797 if (factory == null) { | |
798 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
799 } | |
800 | |
801 Artifact artifact = factory.createArtifact( | |
802 backend.newIdentifier(), | |
803 context, | |
804 callMeta, | |
805 data); | |
806 | |
807 if (artifact == null) { | |
808 throw new ArtifactDatabaseException(CREATION_FAILED); | |
809 } | |
810 | |
811 PersistentArtifact persistentArtifact; | |
812 | |
813 try { | |
814 persistentArtifact = backend.storeInitially( | |
815 artifact, | |
816 factory, | |
817 factory.timeToLiveUntouched(artifact, context)); | |
818 } | |
819 catch (Exception e) { | |
820 logger.error(e.getLocalizedMessage(), e); | |
821 throw new ArtifactDatabaseException(CREATION_FAILED); | |
822 } | |
823 | |
824 ArtifactCallContext cc = new ArtifactCallContext( | |
825 ArtifactDatabaseImpl.this, | |
826 CallContext.NOTHING, | |
827 callMeta, | |
828 persistentArtifact); | |
829 | |
830 try { | |
831 return artifact.describe(null, cc); | |
832 } | |
833 finally { | |
834 cc.postCall(); | |
835 } | |
836 } | |
837 | |
838 | |
839 public Artifact getRawArtifact(String identifier) | |
840 throws ArtifactDatabaseException | |
841 { | |
842 PersistentArtifact artifact = backend.getArtifact(identifier); | |
843 | |
844 if (artifact == null) { | |
845 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
846 } | |
847 | |
848 return artifact.getArtifact(); | |
849 } | |
850 | |
851 | |
852 public Document describe( | |
853 String identifier, | |
854 Document data, | |
855 CallMeta callMeta | |
856 ) | |
857 throws ArtifactDatabaseException | |
858 { | |
859 // TODO: Handle background tasks | |
860 PersistentArtifact artifact = backend.getArtifact(identifier); | |
861 | |
862 if (artifact == null) { | |
863 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
864 } | |
865 | |
866 ArtifactCallContext cc = new ArtifactCallContext( | |
867 ArtifactDatabaseImpl.this, | |
868 CallContext.TOUCH, | |
869 callMeta, | |
870 artifact); | |
871 | |
872 try { | |
873 Artifact art = artifact.getArtifact(); | |
874 Document res = art.describe(data, cc); | |
875 | |
876 if (postDescribeHooks != null) { | |
877 for (Hook hook: postDescribeHooks) { | |
878 hook.execute(art, cc, res); | |
879 } | |
880 } | |
881 | |
882 return res; | |
883 } | |
884 finally { | |
885 cc.postCall(); | |
886 } | |
887 } | |
888 | |
889 public Document advance( | |
890 String identifier, | |
891 Document target, | |
892 CallMeta callMeta | |
893 ) | |
894 throws ArtifactDatabaseException | |
895 { | |
896 // TODO: Handle background tasks | |
897 PersistentArtifact artifact = backend.getArtifact(identifier); | |
898 | |
899 if (artifact == null) { | |
900 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
901 } | |
902 | |
903 ArtifactCallContext cc = new ArtifactCallContext( | |
904 ArtifactDatabaseImpl.this, | |
905 CallContext.STORE, | |
906 callMeta, | |
907 artifact); | |
908 | |
909 try { | |
910 Artifact art = artifact.getArtifact(); | |
911 Document res = art.advance(target, cc); | |
912 | |
913 if (postAdvanceHooks != null) { | |
914 for (Hook hook: postAdvanceHooks) { | |
915 hook.execute(art, cc, res); | |
916 } | |
917 } | |
918 | |
919 return res; | |
920 } | |
921 finally { | |
922 cc.postCall(); | |
923 } | |
924 } | |
925 | |
926 public Document feed(String identifier, Document data, CallMeta callMeta) | |
927 throws ArtifactDatabaseException | |
928 { | |
929 // TODO: Handle background tasks | |
930 PersistentArtifact artifact = backend.getArtifact(identifier); | |
931 | |
932 if (artifact == null) { | |
933 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
934 } | |
935 | |
936 ArtifactCallContext cc = new ArtifactCallContext( | |
937 ArtifactDatabaseImpl.this, | |
938 CallContext.STORE, | |
939 callMeta, | |
940 artifact); | |
941 | |
942 try { | |
943 Artifact art = artifact.getArtifact(); | |
944 Document res = art.feed(data, cc); | |
945 | |
946 if (postFeedHooks != null) { | |
947 for (Hook hook: postFeedHooks) { | |
948 hook.execute(art, cc, res); | |
949 } | |
950 } | |
951 | |
952 return res; | |
953 } | |
954 finally { | |
955 cc.postCall(); | |
956 } | |
957 } | |
958 | |
959 public DeferredOutput out( | |
960 String identifier, | |
961 Document format, | |
962 CallMeta callMeta) | |
963 throws ArtifactDatabaseException | |
964 { | |
965 return out(identifier, null, format, callMeta); | |
966 } | |
967 | |
968 public DeferredOutput out( | |
969 String identifier, | |
970 String type, | |
971 Document format, | |
972 CallMeta callMeta | |
973 ) | |
974 throws ArtifactDatabaseException | |
975 { | |
976 // TODO: Handle background tasks | |
977 PersistentArtifact artifact = backend.getArtifact(identifier); | |
978 | |
979 if (artifact == null) { | |
980 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
981 } | |
982 | |
983 return new DeferredOutputImpl(artifact, type, format, callMeta); | |
984 } | |
985 | |
986 public Document exportArtifact(String artifact, CallMeta callMeta) | |
987 throws ArtifactDatabaseException | |
988 { | |
989 final String [] factoryName = new String[1]; | |
990 | |
991 byte [] bytes = (byte [])backend.loadArtifact( | |
992 artifact, | |
993 new Backend.ArtifactLoader() { | |
994 public Object load( | |
995 ArtifactFactory factory, | |
996 Long ttl, | |
997 byte [] bytes, | |
998 int id | |
999 ) { | |
1000 factoryName[0] = factory.getName(); | |
1001 | |
1002 ArtifactSerializer serializer = factory.getSerializer(); | |
1003 | |
1004 Artifact artifact = serializer.fromBytes(bytes); | |
1005 artifact.cleanup(context); | |
1006 | |
1007 return serializer.toBytes(artifact); | |
1008 } | |
1009 }); | |
1010 | |
1011 if (bytes == null) { | |
1012 throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); | |
1013 } | |
1014 | |
1015 return createExportDocument( | |
1016 factoryName[0], | |
1017 bytes, | |
1018 exportSecret); | |
1019 } | |
1020 | |
1021 /** | |
1022 * Creates an exteral XML representation of an artifact. | |
1023 * @param factoryName The name of the factory which is responsible | |
1024 * for the serialized artifact. | |
1025 * @param artifact The byte data of the artifact itself. | |
1026 * @param secret The signing secret. | |
1027 * @return An XML document containing the external representation | |
1028 * of the artifact. | |
1029 */ | |
1030 protected static Document createExportDocument( | |
1031 String factoryName, | |
1032 byte [] artifact, | |
1033 byte [] secret | |
1034 ) { | |
1035 Document document = XMLUtils.newDocument(); | |
1036 | |
1037 MessageDigest md; | |
1038 try { | |
1039 md = MessageDigest.getInstance(DIGEST_ALGORITHM); | |
1040 } | |
1041 catch (NoSuchAlgorithmException nsae) { | |
1042 logger.error(nsae.getLocalizedMessage(), nsae); | |
1043 return document; | |
1044 } | |
1045 | |
1046 md.update(artifact); | |
1047 md.update(secret); | |
1048 | |
1049 String checksum = Hex.encodeHexString(md.digest()); | |
1050 | |
1051 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1052 document, | |
1053 ArtifactNamespaceContext.NAMESPACE_URI, | |
1054 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1055 | |
1056 Element root = ec.create("action"); | |
1057 document.appendChild(root); | |
1058 | |
1059 Element type = ec.create("type"); | |
1060 ec.addAttr(type, "name", "export", true); | |
1061 root.appendChild(type); | |
1062 | |
1063 Element data = ec.create("data"); | |
1064 ec.addAttr(data, "checksum", checksum, true); | |
1065 ec.addAttr(data, "factory", factoryName, true); | |
1066 data.setTextContent(Base64.encodeBase64String(artifact)); | |
1067 | |
1068 root.appendChild(data); | |
1069 | |
1070 return document; | |
1071 } | |
1072 | |
1073 public Document importArtifact(Document input, CallMeta callMeta) | |
1074 throws ArtifactDatabaseException | |
1075 { | |
1076 String factoryName = XMLUtils.xpathString( | |
1077 input, | |
1078 XPATH_IMPORT_FACTORY, | |
1079 ArtifactNamespaceContext.INSTANCE); | |
1080 | |
1081 ArtifactFactory factory; | |
1082 | |
1083 if (factoryName == null | |
1084 || (factoryName = factoryName.trim()).length() == 0 | |
1085 || (factory = getArtifactFactory(factoryName)) == null) { | |
1086 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1087 } | |
1088 | |
1089 String checksumString = XMLUtils.xpathString( | |
1090 input, | |
1091 XPATH_IMPORT_CHECKSUM, | |
1092 ArtifactNamespaceContext.INSTANCE); | |
1093 | |
1094 byte [] checksum; | |
1095 | |
1096 if (checksumString == null | |
1097 || (checksumString = checksumString.trim()).length() == 0 | |
1098 || (checksum = StringUtils.decodeHex(checksumString)) == null | |
1099 ) { | |
1100 throw new ArtifactDatabaseException(INVALID_CHECKSUM); | |
1101 } | |
1102 | |
1103 checksumString = null; | |
1104 | |
1105 String dataString = XMLUtils.xpathString( | |
1106 input, | |
1107 XPATH_IMPORT_DATA, | |
1108 ArtifactNamespaceContext.INSTANCE); | |
1109 | |
1110 if (dataString == null | |
1111 || (dataString = dataString.trim()).length() == 0) { | |
1112 throw new ArtifactDatabaseException(NO_DATA); | |
1113 } | |
1114 | |
1115 byte [] data = Base64.decodeBase64(dataString); | |
1116 | |
1117 dataString = null; | |
1118 | |
1119 MessageDigest md; | |
1120 try { | |
1121 md = MessageDigest.getInstance(DIGEST_ALGORITHM); | |
1122 } | |
1123 catch (NoSuchAlgorithmException nsae) { | |
1124 logger.error(nsae.getLocalizedMessage(), nsae); | |
1125 return XMLUtils.newDocument(); | |
1126 } | |
1127 | |
1128 md.update(data); | |
1129 md.update(exportSecret); | |
1130 | |
1131 byte [] digest = md.digest(); | |
1132 | |
1133 if (!Arrays.equals(checksum, digest)) { | |
1134 throw new ArtifactDatabaseException(CHECKSUM_MISMATCH); | |
1135 } | |
1136 | |
1137 ArtifactSerializer serializer = factory.getSerializer(); | |
1138 | |
1139 Artifact artifact = serializer.fromBytes(data); data = null; | |
1140 | |
1141 if (artifact == null) { | |
1142 throw new ArtifactDatabaseException(INVALID_ARTIFACT); | |
1143 } | |
1144 | |
1145 artifact.setIdentifier(backend.newIdentifier()); | |
1146 PersistentArtifact persistentArtifact; | |
1147 | |
1148 try { | |
1149 persistentArtifact = backend.storeOrReplace( | |
1150 artifact, | |
1151 factory, | |
1152 factory.timeToLiveUntouched(artifact, context)); | |
1153 } | |
1154 catch (Exception e) { | |
1155 logger.error(e.getLocalizedMessage(), e); | |
1156 throw new ArtifactDatabaseException(CREATION_FAILED); | |
1157 } | |
1158 | |
1159 ArtifactCallContext cc = new ArtifactCallContext( | |
1160 ArtifactDatabaseImpl.this, | |
1161 CallContext.NOTHING, | |
1162 callMeta, | |
1163 persistentArtifact); | |
1164 | |
1165 try { | |
1166 return artifact.describe(input, cc); | |
1167 } | |
1168 finally { | |
1169 cc.postCall(); | |
1170 } | |
1171 } | |
1172 | |
1173 public String [][] serviceNamesAndDescriptions() { | |
1174 return serviceNamesAndDescription; | |
1175 } | |
1176 | |
1177 public Service.Output process( | |
1178 String serviceName, | |
1179 Document input, | |
1180 CallMeta callMeta | |
1181 ) | |
1182 throws ArtifactDatabaseException | |
1183 { | |
1184 Service service = (Service)name2service.get(serviceName); | |
1185 | |
1186 if (service == null) { | |
1187 throw new ArtifactDatabaseException(NO_SUCH_SERVICE); | |
1188 } | |
1189 | |
1190 return service.process(input, context, callMeta); | |
1191 } | |
1192 | |
1193 // User API | |
1194 | |
1195 /** Returns user(s) elements. */ | |
1196 public Document listUsers(CallMeta callMeta) | |
1197 throws ArtifactDatabaseException | |
1198 { | |
1199 UserFactory factory = getUserFactory(); | |
1200 | |
1201 if (factory == null) { | |
1202 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1203 } | |
1204 | |
1205 User [] users = backend.getUsers(factory, context); | |
1206 | |
1207 if (users != null) { | |
1208 logger.debug(users.length + " users found in the backend."); | |
1209 } | |
1210 | |
1211 Document result = XMLUtils.newDocument(); | |
1212 | |
1213 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1214 result, | |
1215 ArtifactNamespaceContext.NAMESPACE_URI, | |
1216 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1217 | |
1218 Element root = ec.create("users"); | |
1219 result.appendChild(root); | |
1220 | |
1221 if(users != null) { | |
1222 for (User user: users) { | |
1223 Element ue = ec.create("user"); | |
1224 ec.addAttr(ue, "uuid", user.identifier(), true); | |
1225 ec.addAttr(ue, "name", user.getName(), true); | |
1226 Element ua = ec.create("account"); | |
1227 ec.addAttr(ua, "name", user.getAccount(), true); | |
1228 ue.appendChild(ua); | |
1229 | |
1230 Document role = user.getRole(); | |
1231 | |
1232 if (role != null) { | |
1233 ue.appendChild(result.importNode(role.getFirstChild(), true)); | |
1234 } | |
1235 | |
1236 root.appendChild(ue); | |
1237 } | |
1238 } | |
1239 | |
1240 return result; | |
1241 } | |
1242 | |
1243 /** Search for a user. */ | |
1244 public Document findUser(Document data, CallMeta callMeta) | |
1245 throws ArtifactDatabaseException | |
1246 { | |
1247 UserFactory factory = getUserFactory(); | |
1248 | |
1249 if (factory == null) { | |
1250 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1251 } | |
1252 | |
1253 String account = XMLUtils.xpathString( | |
1254 data, XPATH_USERACCOUNT_FIND, ArtifactNamespaceContext.INSTANCE); | |
1255 | |
1256 if (account == null || account.length() == 0) { | |
1257 logger.warn("Can't find user without account!"); | |
1258 throw new ArtifactDatabaseException(NO_USERACCOUNT); | |
1259 } | |
1260 | |
1261 User user = backend.findUser(account, factory, context); | |
1262 | |
1263 Document result = XMLUtils.newDocument(); | |
1264 | |
1265 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1266 result, | |
1267 ArtifactNamespaceContext.NAMESPACE_URI, | |
1268 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1269 | |
1270 Element ue = ec.create("user"); | |
1271 | |
1272 if (user != null) { | |
1273 logger.debug(user + " user found in the backend."); | |
1274 | |
1275 ec.addAttr(ue, "uuid", user.identifier(), true); | |
1276 ec.addAttr(ue, "name", user.getName(), true); | |
1277 Element ua = ec.create("account"); | |
1278 ec.addAttr(ua, "name", user.getAccount(), true); | |
1279 ue.appendChild(ua); | |
1280 | |
1281 Document role = user.getRole(); | |
1282 | |
1283 if (role != null) { | |
1284 ue.appendChild(result.importNode(role.getFirstChild(), true)); | |
1285 } | |
1286 } | |
1287 | |
1288 result.appendChild(ue); | |
1289 | |
1290 return result; | |
1291 } | |
1292 | |
1293 public Document createUser(Document data, CallMeta callMeta) | |
1294 throws ArtifactDatabaseException | |
1295 { | |
1296 UserFactory factory = getUserFactory(); | |
1297 | |
1298 if (factory == null) { | |
1299 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1300 } | |
1301 | |
1302 String name = XMLUtils.xpathString( | |
1303 data, XPATH_USERNAME, ArtifactNamespaceContext.INSTANCE); | |
1304 | |
1305 if (name == null || name.length() == 0) { | |
1306 logger.warn("User without username not accepted!"); | |
1307 throw new ArtifactDatabaseException(NO_USERNAME); | |
1308 } | |
1309 | |
1310 String account = XMLUtils.xpathString( | |
1311 data, XPATH_USERACCOUNT, ArtifactNamespaceContext.INSTANCE); | |
1312 | |
1313 if (account == null || account.length() == 0) { | |
1314 logger.warn("User without account not accepted!"); | |
1315 throw new ArtifactDatabaseException(NO_USERACCOUNT); | |
1316 } | |
1317 | |
1318 Node tmp = (Node) XMLUtils.xpath( | |
1319 data, | |
1320 XPATH_USERROLE, | |
1321 XPathConstants.NODE, | |
1322 ArtifactNamespaceContext.INSTANCE); | |
1323 | |
1324 Document role = XMLUtils.newDocument(); | |
1325 | |
1326 if (tmp != null) { | |
1327 Node clone = role.importNode(tmp, true); | |
1328 role.appendChild(clone); | |
1329 } | |
1330 | |
1331 User newUser = null; | |
1332 | |
1333 try { | |
1334 newUser = backend.createUser(name, account, role, userFactory, context); | |
1335 } | |
1336 catch (Exception e) { | |
1337 logger.error(e.getMessage(), e); | |
1338 throw new ArtifactDatabaseException(USER_CREATION_FAILED); | |
1339 } | |
1340 | |
1341 Document result = XMLUtils.newDocument(); | |
1342 | |
1343 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1344 result, | |
1345 ArtifactNamespaceContext.NAMESPACE_URI, | |
1346 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1347 | |
1348 Element root = ec.create("result"); | |
1349 | |
1350 if (newUser != null) { | |
1351 root.setTextContent(OPERATION_SUCCESSFUL); | |
1352 } | |
1353 else { | |
1354 root.setTextContent(OPERATION_FAILURE); | |
1355 } | |
1356 | |
1357 result.appendChild(root); | |
1358 | |
1359 return result; | |
1360 } | |
1361 | |
1362 public Document deleteUser(String userId, CallMeta callMeta) | |
1363 throws ArtifactDatabaseException | |
1364 { | |
1365 logger.debug("Delete user: " + userId); | |
1366 | |
1367 Document result = XMLUtils.newDocument(); | |
1368 | |
1369 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1370 result, | |
1371 ArtifactNamespaceContext.NAMESPACE_URI, | |
1372 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1373 | |
1374 Element root = ec.create("result"); | |
1375 result.appendChild(root); | |
1376 | |
1377 boolean success = backend.deleteUser(userId); | |
1378 | |
1379 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1380 | |
1381 return result; | |
1382 } | |
1383 | |
1384 | |
1385 // Collection API | |
1386 | |
1387 public Document getCollectionsMasterArtifact( | |
1388 String collectionId, | |
1389 CallMeta meta) | |
1390 throws ArtifactDatabaseException | |
1391 { | |
1392 Document result = XMLUtils.newDocument(); | |
1393 String masterUUID = backend.getMasterArtifact(collectionId); | |
1394 | |
1395 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1396 result, | |
1397 ArtifactNamespaceContext.NAMESPACE_URI, | |
1398 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1399 | |
1400 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1401 | |
1402 if (acf == null) { | |
1403 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1404 } | |
1405 | |
1406 UserFactory uf = getUserFactory(); | |
1407 if (uf == null) { | |
1408 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1409 } | |
1410 | |
1411 ArtifactCollection c = backend.getCollection( | |
1412 collectionId, acf, uf, context); | |
1413 | |
1414 if (c == null) { | |
1415 logger.warn("No collection found with identifier: " + collectionId); | |
1416 throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); | |
1417 } | |
1418 | |
1419 Element root = ec.create("artifact-collection"); | |
1420 ec.addAttr(root, "name", c.getName(), true); | |
1421 ec.addAttr(root, "uuid", c.identifier(), true); | |
1422 ec.addAttr(root, "ttl", String.valueOf(c.getTTL()), true); | |
1423 | |
1424 Date creationTime = c.getCreationTime(); | |
1425 String creation = creationTime != null | |
1426 ? Long.toString(creationTime.getTime()) | |
1427 : ""; | |
1428 | |
1429 ec.addAttr(root, "creation", creation, true); | |
1430 result.appendChild(root); | |
1431 | |
1432 if (masterUUID == null || masterUUID.length() == 0) { | |
1433 logger.debug("No master for the collection existing."); | |
1434 return result; | |
1435 } | |
1436 | |
1437 Element master = ec.create("artifact"); | |
1438 ec.addAttr(master, "uuid", masterUUID, true); | |
1439 | |
1440 root.appendChild(master); | |
1441 | |
1442 return result; | |
1443 } | |
1444 | |
1445 public Document listCollections(String userId, CallMeta callMeta) | |
1446 throws ArtifactDatabaseException | |
1447 { | |
1448 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1449 UserFactory uf = getUserFactory(); | |
1450 | |
1451 if (acf == null || uf == null) { | |
1452 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1453 } | |
1454 | |
1455 logger.debug("Fetch the list of collection for user: " + userId); | |
1456 | |
1457 ArtifactCollection [] ac = backend.listCollections( | |
1458 userId, | |
1459 null, // XXX: fetch from REST | |
1460 acf, uf, | |
1461 context); | |
1462 | |
1463 Document result = XMLUtils.newDocument(); | |
1464 | |
1465 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1466 result, | |
1467 ArtifactNamespaceContext.NAMESPACE_URI, | |
1468 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1469 | |
1470 Element root = ec.create("artifact-collections"); | |
1471 result.appendChild(root); | |
1472 | |
1473 if (ac == null || ac.length == 0) { | |
1474 logger.debug("No collections for the user existing."); | |
1475 | |
1476 return result; | |
1477 } | |
1478 | |
1479 logger.debug("Found " + ac.length + " collections of the user."); | |
1480 | |
1481 for (ArtifactCollection c: ac) { | |
1482 Element collection = ec.create("artifact-collection"); | |
1483 ec.addAttr(collection, "name", c.getName(), true); | |
1484 ec.addAttr(collection, "uuid", c.identifier(), true); | |
1485 ec.addAttr(collection, "ttl", String.valueOf(c.getTTL()), true); | |
1486 | |
1487 Date creationTime = c.getCreationTime(); | |
1488 String creation = creationTime != null | |
1489 ? Long.toString(creationTime.getTime()) | |
1490 : ""; | |
1491 | |
1492 ec.addAttr(collection, "creation", creation, true); | |
1493 | |
1494 root.appendChild(collection); | |
1495 } | |
1496 | |
1497 return result; | |
1498 } | |
1499 | |
1500 public Document createCollection(String ownerId, Document data, | |
1501 CallMeta callMeta) | |
1502 throws ArtifactDatabaseException | |
1503 { | |
1504 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1505 | |
1506 if (acf == null) { | |
1507 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1508 } | |
1509 | |
1510 String name = XMLUtils.xpathString( | |
1511 data, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); | |
1512 | |
1513 logger.debug("Create new collection with name: " + name); | |
1514 | |
1515 Document attr = null; | |
1516 | |
1517 Node attrNode = (Node) XMLUtils.xpath( | |
1518 data, | |
1519 XPATH_COLLECTION_ATTRIBUTE, | |
1520 XPathConstants.NODE, | |
1521 ArtifactNamespaceContext.INSTANCE); | |
1522 | |
1523 if (attrNode != null) { | |
1524 attr = XMLUtils.newDocument(); | |
1525 attr.appendChild(attr.importNode(attrNode, true)); | |
1526 } | |
1527 | |
1528 ArtifactCollection ac = backend.createCollection( | |
1529 ownerId, name, acf, attr, context); | |
1530 | |
1531 if (ac == null) { | |
1532 throw new ArtifactDatabaseException(COLLECTION_CREATION_FAILED); | |
1533 } | |
1534 | |
1535 Document result = XMLUtils.newDocument(); | |
1536 | |
1537 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1538 result, | |
1539 ArtifactNamespaceContext.NAMESPACE_URI, | |
1540 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1541 | |
1542 Element root = ec.create("result"); | |
1543 result.appendChild(root); | |
1544 | |
1545 Element acElement = ec.create("artifact-collection"); | |
1546 ec.addAttr(acElement, "uuid", ac.identifier(), true); | |
1547 ec.addAttr(acElement, "ttl", String.valueOf(ac.getTTL()), true); | |
1548 | |
1549 root.appendChild(acElement); | |
1550 | |
1551 return result; | |
1552 } | |
1553 | |
1554 public Document deleteCollection(String collectionId, CallMeta callMeta) | |
1555 throws ArtifactDatabaseException | |
1556 { | |
1557 logger.debug("Delete collection: " + collectionId); | |
1558 | |
1559 Document result = XMLUtils.newDocument(); | |
1560 | |
1561 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1562 result, | |
1563 ArtifactNamespaceContext.NAMESPACE_URI, | |
1564 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1565 | |
1566 Element root = ec.create("result"); | |
1567 result.appendChild(root); | |
1568 | |
1569 boolean success = backend.deleteCollection(collectionId); | |
1570 | |
1571 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1572 | |
1573 return result; | |
1574 } | |
1575 | |
1576 public Document describeCollection(String collectionId, CallMeta callMeta) | |
1577 throws ArtifactDatabaseException | |
1578 { | |
1579 logger.debug("Describe collection: " + collectionId); | |
1580 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1581 | |
1582 if (acf == null) { | |
1583 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1584 } | |
1585 | |
1586 UserFactory uf = getUserFactory(); | |
1587 if (uf == null) { | |
1588 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1589 } | |
1590 | |
1591 ArtifactCollection c = backend.getCollection( | |
1592 collectionId, acf, uf, context); | |
1593 | |
1594 if (c == null) { | |
1595 logger.warn("No collection found with identifier: " + collectionId); | |
1596 throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); | |
1597 } | |
1598 | |
1599 CollectionCallContext cc = new CollectionCallContext( | |
1600 ArtifactDatabaseImpl.this, | |
1601 CallContext.NOTHING, | |
1602 callMeta, | |
1603 c); | |
1604 | |
1605 try { | |
1606 return c.describe(cc); | |
1607 } | |
1608 finally { | |
1609 cc.postCall(); | |
1610 } | |
1611 } | |
1612 | |
1613 | |
1614 public Document getCollectionAttribute(String collectionId, CallMeta meta) | |
1615 throws ArtifactDatabaseException | |
1616 { | |
1617 logger.debug("Fetch collection attribute for: " + collectionId); | |
1618 | |
1619 return backend.getCollectionAttribute(collectionId); | |
1620 } | |
1621 | |
1622 | |
1623 public Document setCollectionAttribute( | |
1624 String collectionId, | |
1625 CallMeta meta, | |
1626 Document attribute) | |
1627 throws ArtifactDatabaseException | |
1628 { | |
1629 logger.debug("Set new attribute for the collection: " + collectionId); | |
1630 | |
1631 Document result = XMLUtils.newDocument(); | |
1632 | |
1633 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1634 result, | |
1635 ArtifactNamespaceContext.NAMESPACE_URI, | |
1636 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1637 | |
1638 Element root = ec.create("result"); | |
1639 result.appendChild(root); | |
1640 | |
1641 boolean success = backend.setCollectionAttribute( | |
1642 collectionId, attribute); | |
1643 | |
1644 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1645 | |
1646 return result; | |
1647 } | |
1648 | |
1649 public Document getCollectionItemAttribute(String collectionId, String artifactId, | |
1650 CallMeta callMeta) throws ArtifactDatabaseException | |
1651 { | |
1652 logger.debug("Fetch the attribute for the artifact: " + artifactId); | |
1653 | |
1654 return backend.getCollectionItemAttribute(collectionId, artifactId); | |
1655 } | |
1656 | |
1657 public Document setCollectionItemAttribute(String collectionId, String artifactId, | |
1658 Document source, CallMeta callMeta) | |
1659 throws ArtifactDatabaseException | |
1660 { | |
1661 logger.debug("Set the attribute for the artifact: " + artifactId); | |
1662 | |
1663 Document attribute = null; | |
1664 | |
1665 Node attr = (Node) XMLUtils.xpath( | |
1666 source, | |
1667 XPATH_COLLECTION_ITEM_ATTRIBUTE, | |
1668 XPathConstants.NODE, | |
1669 ArtifactNamespaceContext.INSTANCE); | |
1670 | |
1671 if (attr != null) { | |
1672 attribute = XMLUtils.newDocument(); | |
1673 attribute.appendChild(attribute.importNode(attr, true)); | |
1674 } | |
1675 | |
1676 Document result = XMLUtils.newDocument(); | |
1677 | |
1678 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1679 result, | |
1680 ArtifactNamespaceContext.NAMESPACE_URI, | |
1681 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1682 | |
1683 Element root = ec.create("result"); | |
1684 result.appendChild(root); | |
1685 | |
1686 boolean success = backend.setCollectionItemAttribute( | |
1687 collectionId, artifactId, attribute); | |
1688 | |
1689 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1690 | |
1691 return result; | |
1692 } | |
1693 | |
1694 public Document addCollectionArtifact( | |
1695 String collectionId, | |
1696 String artifactId, | |
1697 Document input, | |
1698 CallMeta callMeta) | |
1699 throws ArtifactDatabaseException | |
1700 { | |
1701 logger.debug( | |
1702 "Add artifact '" + artifactId + "' collection '" +collectionId+"'"); | |
1703 | |
1704 Document attr = XMLUtils.newDocument(); | |
1705 | |
1706 Node attrNode = (Node) XMLUtils.xpath( | |
1707 input, | |
1708 XPATH_COLLECTION_ITEM_ATTRIBUTE, | |
1709 XPathConstants.NODE, | |
1710 ArtifactNamespaceContext.INSTANCE); | |
1711 | |
1712 if (attrNode != null) { | |
1713 attr.appendChild(attr.importNode(attrNode, true)); | |
1714 } | |
1715 | |
1716 boolean success = backend.addCollectionArtifact( | |
1717 collectionId, | |
1718 artifactId, | |
1719 attr); | |
1720 | |
1721 if (!success) { | |
1722 Document result = XMLUtils.newDocument(); | |
1723 | |
1724 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1725 result, | |
1726 ArtifactNamespaceContext.NAMESPACE_URI, | |
1727 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1728 | |
1729 Element root = ec.create("result"); | |
1730 result.appendChild(root); | |
1731 | |
1732 root.setTextContent(OPERATION_FAILURE); | |
1733 | |
1734 return result; | |
1735 } | |
1736 | |
1737 return describeCollection(collectionId, callMeta); | |
1738 } | |
1739 | |
1740 public Document removeCollectionArtifact(String collectionId, String artifactId, | |
1741 CallMeta callMeta) throws ArtifactDatabaseException | |
1742 { | |
1743 logger.debug( | |
1744 "Remove artifact '" + artifactId + "' from collection '" + | |
1745 collectionId + "'"); | |
1746 | |
1747 Document attr = XMLUtils.newDocument(); | |
1748 | |
1749 boolean success = backend.removeCollectionArtifact( | |
1750 collectionId, | |
1751 artifactId); | |
1752 | |
1753 Document result = XMLUtils.newDocument(); | |
1754 | |
1755 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1756 result, | |
1757 ArtifactNamespaceContext.NAMESPACE_URI, | |
1758 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1759 | |
1760 Element root = ec.create("result"); | |
1761 result.appendChild(root); | |
1762 | |
1763 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1764 | |
1765 return result; | |
1766 } | |
1767 | |
1768 public Document listCollectionArtifacts(String collectionId, | |
1769 CallMeta callMeta) throws ArtifactDatabaseException | |
1770 { | |
1771 CollectionItem[] items = backend.listCollectionArtifacts(collectionId); | |
1772 | |
1773 Document result = XMLUtils.newDocument(); | |
1774 | |
1775 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1776 result, | |
1777 ArtifactNamespaceContext.NAMESPACE_URI, | |
1778 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1779 | |
1780 Element root = ec.create("result"); | |
1781 Element ac = ec.create("artifact-collection"); | |
1782 ec.addAttr(ac, "uuid", collectionId, true); | |
1783 | |
1784 for (CollectionItem item: items) { | |
1785 Element i = ec.create("collection-item"); | |
1786 Element attr = ec.create("attribute"); | |
1787 ec.addAttr(i, "uuid", item.getArtifactIdentifier(), true); | |
1788 | |
1789 Document attribute = item.getAttribute(); | |
1790 if (attribute != null) { | |
1791 Node firstChild = attribute.getFirstChild(); | |
1792 attr.appendChild(result.importNode(firstChild, true)); | |
1793 } | |
1794 else { | |
1795 logger.debug("No attributes for the collection item!"); | |
1796 } | |
1797 | |
1798 i.appendChild(attr); | |
1799 ac.appendChild(i); | |
1800 } | |
1801 | |
1802 root.appendChild(ac); | |
1803 result.appendChild(root); | |
1804 | |
1805 return result; | |
1806 } | |
1807 | |
1808 public Document setCollectionTTL(String uuid, Document doc, CallMeta meta) | |
1809 throws ArtifactDatabaseException | |
1810 { | |
1811 Document result = XMLUtils.newDocument(); | |
1812 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1813 result, | |
1814 ArtifactNamespaceContext.NAMESPACE_URI, | |
1815 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1816 | |
1817 Element root = ec.create("result"); | |
1818 result.appendChild(root); | |
1819 | |
1820 String tmp = XMLUtils.xpathString( | |
1821 doc, XPATH_COLLECTION_TTL, ArtifactNamespaceContext.INSTANCE); | |
1822 | |
1823 logger.info("Set TTL of artifact collection '" + uuid + "' to: " + tmp); | |
1824 | |
1825 if (tmp == null || tmp.length() == 0) { | |
1826 logger.warn("No ttl for this collection specified."); | |
1827 root.setTextContent(OPERATION_FAILURE); | |
1828 | |
1829 return result; | |
1830 } | |
1831 | |
1832 Long ttl = null; | |
1833 if ((tmp = tmp.toUpperCase()).equals("INF")) { | |
1834 ttl = null; | |
1835 } | |
1836 else if (tmp.equals("DEFAULT")) { | |
1837 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1838 ttl = acf.timeToLiveUntouched(null, context); | |
1839 } | |
1840 else { | |
1841 try { | |
1842 ttl = Long.valueOf(tmp); | |
1843 | |
1844 if (ttl < 0) { | |
1845 throw new NumberFormatException("Negative value."); | |
1846 } | |
1847 } | |
1848 catch (NumberFormatException nfe) { | |
1849 logger.error("Could not determine TTL", nfe); | |
1850 root.setTextContent(OPERATION_FAILURE); | |
1851 return result; | |
1852 } | |
1853 } | |
1854 | |
1855 boolean success = backend.setCollectionTTL(uuid, ttl); | |
1856 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1857 | |
1858 return result; | |
1859 } | |
1860 | |
1861 | |
1862 public Document setCollectionName(String uuid, Document doc, CallMeta meta) | |
1863 throws ArtifactDatabaseException | |
1864 { | |
1865 Document result = XMLUtils.newDocument(); | |
1866 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
1867 result, | |
1868 ArtifactNamespaceContext.NAMESPACE_URI, | |
1869 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
1870 | |
1871 Element root = ec.create("result"); | |
1872 result.appendChild(root); | |
1873 | |
1874 String name = XMLUtils.xpathString( | |
1875 doc, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); | |
1876 | |
1877 logger.info("Set name of collection '" + uuid + "' to: " + name); | |
1878 | |
1879 if (name == null || name.length() == 0) { | |
1880 logger.warn("The new name is emtpy. No new name set!"); | |
1881 root.setTextContent(OPERATION_FAILURE); | |
1882 return result; | |
1883 } | |
1884 | |
1885 boolean success = backend.setCollectionName(uuid, name); | |
1886 root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); | |
1887 | |
1888 return result; | |
1889 } | |
1890 | |
1891 | |
1892 public DeferredOutput outCollection( | |
1893 String collectionId, | |
1894 Document format, | |
1895 CallMeta callMeta) | |
1896 throws ArtifactDatabaseException | |
1897 { | |
1898 return outCollection(collectionId, null, format, callMeta); | |
1899 } | |
1900 | |
1901 public DeferredOutput outCollection( | |
1902 String collectionId, | |
1903 String type, | |
1904 Document format, | |
1905 CallMeta callMeta) | |
1906 throws ArtifactDatabaseException | |
1907 { | |
1908 ArtifactCollectionFactory acf = getArtifactCollectionFactory(); | |
1909 | |
1910 if (acf == null) { | |
1911 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1912 } | |
1913 | |
1914 UserFactory uf = getUserFactory(); | |
1915 if (uf == null) { | |
1916 throw new ArtifactDatabaseException(NO_SUCH_FACTORY); | |
1917 } | |
1918 | |
1919 ArtifactCollection c = backend.getCollection( | |
1920 collectionId, acf, uf, context); | |
1921 | |
1922 if (c == null) { | |
1923 logger.warn("No collection found with identifier: " + collectionId); | |
1924 throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); | |
1925 } | |
1926 | |
1927 return new DeferredCollectionOutputImpl(c, type, format, callMeta); | |
1928 } | |
1929 | |
1930 protected void initCallContext(CallContext cc) { | |
1931 logger.debug("initCallContext"); | |
1932 if (callContextListener != null) { | |
1933 callContextListener.init(cc); | |
1934 } | |
1935 } | |
1936 | |
1937 protected void closeCallContext(CallContext cc) { | |
1938 logger.debug("closeCallContext"); | |
1939 if (callContextListener != null) { | |
1940 callContextListener.close(cc); | |
1941 } | |
1942 } | |
1943 | |
1944 @Override | |
1945 public void loadAllArtifacts(ArtifactLoadedCallback callback) | |
1946 throws ArtifactDatabaseException | |
1947 { | |
1948 logger.debug("loadAllArtifacts"); | |
1949 boolean success = backend.loadAllArtifacts(callback); | |
1950 if (!success) { | |
1951 throw new ArtifactDatabaseException(INTERNAL_ERROR); | |
1952 } | |
1953 } | |
1954 | |
1955 public void start() { | |
1956 if (lifetimeListeners == null || lifetimeListeners.isEmpty()) { | |
1957 return; | |
1958 } | |
1959 | |
1960 for (LifetimeListener ltl: lifetimeListeners) { | |
1961 ltl.systemUp(context); | |
1962 } | |
1963 | |
1964 logger.debug("all lifetime listeners started"); | |
1965 | |
1966 Runtime.getRuntime().addShutdownHook(new Thread() { | |
1967 @Override | |
1968 public void run() { | |
1969 for (LifetimeListener ltl: lifetimeListeners) { | |
1970 ltl.systemDown(context); | |
1971 } | |
1972 } | |
1973 }); | |
1974 } | |
1975 } | |
1976 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |