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 :

http://dive4elements.wald.intevation.org