1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 package org.astrogrid.filestore.server.repository ;
124
125 import org.apache.commons.logging.Log ;
126 import org.apache.commons.logging.LogFactory ;
127
128 import java.net.URL ;
129 import java.net.URLConnection ;
130 import java.net.MalformedURLException ;
131
132 import java.util.Date ;
133
134 import java.io.File ;
135 import java.io.IOException ;
136 import java.io.InputStream ;
137 import java.io.OutputStream ;
138 import java.io.FileInputStream ;
139 import java.io.FileOutputStream ;
140 import java.io.FileNotFoundException ;
141
142 import org.astrogrid.filestore.common.file.FileProperty ;
143 import org.astrogrid.filestore.common.file.FileProperties ;
144 import org.astrogrid.filestore.common.file.FileIdentifier ;
145
146 import org.astrogrid.filestore.common.ivorn.FileStoreIvornFactory ;
147
148 import org.astrogrid.filestore.common.transfer.TransferUtil ;
149 import org.astrogrid.filestore.common.exception.FileStoreException ;
150 import org.astrogrid.filestore.common.exception.FileStoreNotFoundException ;
151 import org.astrogrid.filestore.common.exception.FileStoreIdentifierException ;
152 import org.astrogrid.filestore.common.exception.FileStoreServiceException ;
153 import org.astrogrid.filestore.common.exception.FileStoreTransferException ;
154
155 import org.astrogrid.filestore.common.FileStoreConfig ;
156 import org.astrogrid.filestore.common.FileStoreDateFormat;
157
158 import org.astrogrid.filestore.server.FileStoreConfigImpl ;
159
160 /***
161 * A factory class for creating and storing file containers.
162 *
163 */
164 public class RepositoryImpl
165 implements Repository
166 {
167 /***
168 * Our debug logger.
169 *
170 */
171 private static Log log = LogFactory.getLog(RepositoryImpl.class);
172
173 /***
174 * The size of our copy buffer (8k bytes).
175 *
176 */
177 private static final int COPY_BUFFER_SIZE = 8 * 1024 ;
178
179 /***
180 * Public constructor.
181 *
182 */
183 public RepositoryImpl(FileStoreConfig config)
184 {
185 if (null == config)
186 {
187 throw new IllegalArgumentException(
188 "Null repository config"
189 ) ;
190 }
191 this.config = config ;
192 }
193
194 /***
195 * Reference to our repository config.
196 *
197 */
198 protected FileStoreConfig config ;
199
200 /***
201 * Factory method to create a new container.
202 * @param properties An optional array of FileProperty(ies) describing the file contents.
203 * @return A new file container.
204 * @throws FileStoreServiceException if unable handle the request.
205 *
206 */
207 public RepositoryContainer create(FileProperty[] properties)
208 throws FileStoreServiceException
209 {
210 return create(
211 new FileProperties(
212 properties
213 )
214 ) ;
215 }
216
217 /***
218 * Factory method to create a new container.
219 * @param properties An optional FileProperties describing the file contents.
220 * @return A new file container.
221 * @throws FileStoreServiceException if unable handle the request.
222 *
223 */
224 public RepositoryContainer create(FileProperties properties)
225 throws FileStoreServiceException
226 {
227
228
229 return new RepositoryContainerImpl(
230 properties
231 ) ;
232 }
233
234 /***
235 * Locate an existing container.
236 * @param ident The identifier of the container.
237 * @return The file container, if it exists.
238 * @throws FileStoreIdentifierException if the identifier is null or not valid.
239 * @throws FileStoreNotFoundException if unable to locate the file.
240 * @throws FileStoreServiceException if unable handle the request.
241 * @todo Use FileIdentifier to ensure the ident is valid (which it don't yet).
242 *
243 */
244 public RepositoryContainer load(String ident)
245 throws FileStoreServiceException, FileStoreNotFoundException, FileStoreIdentifierException
246 {
247
248
249 if (null == ident)
250 {
251 throw new FileStoreIdentifierException(
252 FileStoreIdentifierException.NULL_IDENT_MESSAGE
253 ) ;
254 }
255
256
257 return new RepositoryContainerImpl(
258 ident
259 ) ;
260 }
261
262 /***
263 * Copy an existing container.
264 * @param ident The identifier of the container.
265 * @return The new file container.
266 * @throws FileStoreIdentifierException if the identifier is null or not valid.
267 * @throws FileStoreTransferException If unable to transfer the data.
268 * @throws FileStoreNotFoundException if unable to locate the file.
269 * @throws FileStoreServiceException if unable handle the request.
270 *
271 */
272 public RepositoryContainer duplicate(String ident)
273 throws FileStoreServiceException, FileStoreNotFoundException, FileStoreIdentifierException, FileStoreTransferException
274 {
275
276
277 RepositoryContainer original = new RepositoryContainerImpl(
278 ident
279 ) ;
280
281
282 RepositoryContainer duplicate = new RepositoryContainerImpl(
283 original.properties()
284 ) ;
285
286
287 duplicate.importData(
288 original.getDataInputStream()
289 ) ;
290
291
292 return duplicate ;
293 }
294
295 /***
296 * Inner class to implement a container.
297 *
298 */
299 protected class RepositoryContainerImpl
300 implements RepositoryContainer
301 {
302 /***
303 * The file identifier.
304 *
305 */
306 private FileIdentifier ident ;
307
308 /***
309 * Access to our identifier.
310 *
311 */
312 public FileIdentifier ident()
313 {
314 return this.ident ;
315 }
316
317 /***
318 * The file properties.
319 *
320 */
321 private FileProperties properties ;
322
323 /***
324 * Access to our properties.
325 *
326 */
327 public FileProperties properties()
328 {
329 return this.properties ;
330 }
331
332 /***
333 * Constructor to create a new container.
334 * @param properties The file properties.
335 * @throws FileStoreServiceException If unable create the container.
336 *
337 */
338 public RepositoryContainerImpl(FileProperties properties)
339 throws FileStoreServiceException
340 {
341
342
343 this.properties = new FileProperties(
344 properties
345 ) ;
346
347
348 this.ident = new FileIdentifier() ;
349
350
351 FileStoreDateFormat formatter = new FileStoreDateFormat();
352
353
354 this.properties.setProperty(
355 FileProperties.FILE_CREATED_DATE_PROPERTY,
356 formatter.format(
357 new Date()
358 )
359 ) ;
360
361
362 this.update() ;
363 }
364
365 /***
366 * Constructor to load an existing container.
367 * @param ident The identifier of the container.
368 * @throws FileStoreIdentifierException if the identifier is null or not valid.
369 * @throws FileStoreNotFoundException if unable to locate the file.
370 * @throws FileStoreServiceException if unable handle the request.
371 *
372 */
373 public RepositoryContainerImpl(String ident)
374 throws FileStoreServiceException, FileStoreNotFoundException, FileStoreIdentifierException
375 {
376
377
378 this.ident = new FileIdentifier(ident) ;
379
380
381 this.properties = new FileProperties() ;
382
383
384 this.load() ;
385 }
386
387 /***
388 * Generate the properties file path.
389 * @throws FileStoreServiceException if unable handle the request.
390 *
391 */
392 public File getInfoFile()
393 throws FileStoreServiceException
394 {
395 return new File(
396 config.getInfoDirectory(),
397 this.ident.toString() + ".inf"
398 ) ;
399 }
400
401 /***
402 * Generate the data file path.
403 * @throws FileStoreServiceException if unable handle the request.
404 *
405 */
406 public File getDataFile()
407 throws FileStoreServiceException
408 {
409 return new File(
410 config.getDataDirectory(),
411 this.ident.toString() + ".dat"
412 ) ;
413 }
414
415 /***
416 * Get the data file size.
417 *
418 */
419 public long getDataFileSize()
420 {
421 try {
422 return getDataFile().length() ;
423 }
424 catch (FileStoreServiceException ouch)
425 {
426 return 0L ;
427 }
428 }
429
430 /***
431 * Update the properties.
432 *
433 */
434 public void update()
435 throws FileStoreServiceException
436 {
437
438
439 if (null == this.properties)
440 {
441 this.properties = new FileProperties() ;
442 }
443
444
445 try {
446
447
448 this.properties.setProperty(
449 FileProperties.CONTENT_SIZE_PROPERTY,
450 String.valueOf(
451 this.getDataFileSize()
452 )
453 ) ;
454
455
456 this.properties.setStoreResourceIvorn(
457 config.getResourceIvorn(
458 ident.toString()
459 )
460 ) ;
461
462
463 this.properties.setProperty(
464 FileProperties.STORE_RESOURCE_URL,
465 config.getResourceUrl(
466 ident.toString()
467 ).toString()
468 ) ;
469
470
471 FileStoreDateFormat formatter = new FileStoreDateFormat();
472
473
474 this.properties.setProperty(
475 FileProperties.FILE_MODIFIED_DATE_PROPERTY,
476 formatter.format(
477 new Date()
478 )
479 ) ;
480 }
481
482
483 finally {
484 this.save() ;
485 }
486 }
487
488 /***
489 * Load the file properties.
490 * @throws FileStoreServiceException If unable to load the info.
491 * @throws FileStoreNotFoundException If unable to locate the file.
492 *
493 */
494 protected void load()
495 throws FileStoreServiceException, FileStoreNotFoundException
496 {
497 InputStream input = null ;
498 try {
499 input = new FileInputStream(
500 this.getInfoFile()
501 ) ;
502 this.properties.load(
503 input
504 ) ;
505 }
506 catch (FileNotFoundException ouch)
507 {
508 throw new FileStoreNotFoundException(
509 this.ident.toString()
510 ) ;
511 }
512 catch (IOException ouch)
513 {
514 throw new FileStoreServiceException(
515 "Unable to load info file",
516 ouch
517 ) ;
518 }
519 finally
520 {
521 if (null != input)
522 {
523 try {
524 input.close() ;
525 }
526 catch (IOException ouch)
527 {
528 log.warn("----") ;
529 log.warn("RepositoryContainerImpl.load") ;
530 log.warn("Failed to close input stream") ;
531 log.warn("----") ;
532 }
533 }
534 }
535 }
536
537 /***
538 * Save the file properties.
539 * @throws FileStoreServiceException If unable to save the info.
540 *
541 */
542 protected void save()
543 throws FileStoreServiceException
544 {
545 OutputStream output = null ;
546 try {
547 output = new FileOutputStream(
548 this.getInfoFile()
549 ) ;
550 this.properties.save(
551 output
552 ) ;
553 }
554 catch (IOException ouch)
555 {
556 throw new FileStoreServiceException(
557 "Unable to save info file",
558 ouch
559 ) ;
560 }
561 finally
562 {
563 if (null != output)
564 {
565 try {
566 output.close() ;
567 }
568 catch (IOException ouch)
569 {
570 log.warn("----") ;
571 log.warn("RepositoryContainerImpl.save") ;
572 log.warn("Failed to close output stream") ;
573 log.warn("----") ;
574 }
575 }
576 }
577 }
578
579 /***
580 * Delete the container file(s).
581 * @throws FileStoreNotFoundException If unable to locate the file.
582 * @throws FileStoreServiceException If unable to complete the action.
583 *
584 */
585 public void delete()
586 throws FileStoreServiceException
587 {
588 this.getDataFile().delete() ;
589 this.getInfoFile().delete() ;
590 }
591
592 /***
593 * Import an array of bytes.
594 * @param bytes The array of bytes to import into the container.
595 * @throws FileStoreServiceException If unable to complete the action.
596 *
597 */
598 public void importBytes(byte[] bytes)
599 throws FileStoreServiceException
600 {
601
602
603 OutputStream output = null ;
604 try {
605 output = this.getDataOutputStream() ;
606 output.write(bytes) ;
607 }
608 catch (IOException ouch)
609 {
610 throw new FileStoreServiceException(
611 "Unable to write to data file",
612 ouch
613 ) ;
614 }
615 finally
616 {
617
618
619 this.update() ;
620
621
622 try {
623 if (null != output)
624 {
625 output.close() ;
626 }
627 }
628 catch (IOException ouch)
629 {
630 log.warn("----") ;
631 log.warn("RepositoryContainerImpl.importBytes") ;
632 log.warn("Failed to close output stream") ;
633 log.warn("----") ;
634 }
635 }
636 }
637
638 /***
639 * Export our contents as bytes.
640 * @return An array of bytes from the container contents.
641 * @throws FileStoreNotFoundException If unable to locate the file.
642 * @throws FileStoreServiceException If unable to complete the action.
643 * @todo Deprecate this soon, as it potentially allocates a HUGE buffer in memory.
644 *
645 */
646 public byte[] exportBytes()
647 throws FileStoreServiceException, FileStoreNotFoundException
648 {
649 int size = (int) this.getDataFile().length() ;
650 byte[] buffer = new byte[size] ;
651 InputStream input = null ;
652 try {
653 input = this.getDataInputStream() ;
654 input.read(buffer) ;
655 }
656 catch (IOException ouch)
657 {
658 throw new FileStoreServiceException(
659 "Unable to read file data",
660 ouch
661 ) ;
662 }
663 finally
664 {
665 try {
666 if (null != input)
667 {
668 input.close() ;
669 }
670 }
671 catch (IOException ouch)
672 {
673 log.warn("----") ;
674 log.warn("RepositoryContainerImpl.exportBytes") ;
675 log.warn("Failed to close input stream") ;
676 log.warn("----") ;
677 }
678 }
679 return buffer ;
680 }
681
682 /***
683 * Append an array of bytes.
684 * @param bytes The array of bytes to append to the container.
685 * @throws FileStoreNotFoundException If unable to locate the file.
686 * @throws FileStoreServiceException If unable to complete the action.
687 *
688 */
689 public void appendBytes(byte[] bytes)
690 throws FileStoreServiceException, FileStoreNotFoundException
691 {
692
693
694 OutputStream output = null ;
695 try {
696 output = this.getDataOutputStream(true) ;
697 output.write(bytes) ;
698 output.close() ;
699 }
700 catch (IOException ouch)
701 {
702 throw new FileStoreServiceException(
703 "Unable to write data file",
704 ouch
705 ) ;
706 }
707 finally
708 {
709
710
711 this.update() ;
712
713
714 try {
715 if (null != output)
716 {
717 output.close() ;
718 }
719 }
720 catch (IOException ouch)
721 {
722 log.warn("----") ;
723 log.warn("RepositoryContainerImpl.appendBytes") ;
724 log.warn("Failed to close output stream") ;
725 log.warn("----") ;
726 }
727 }
728 }
729
730 /***
731 * Get an input stream to the container contents.
732 * @throws FileStoreNotFoundException If unable to locate the file.
733 * @throws FileStoreServiceException If unable to open the file.
734 *
735 */
736 public InputStream getDataInputStream()
737 throws FileStoreServiceException, FileStoreNotFoundException
738 {
739 try {
740 return new FileInputStream(
741 this.getDataFile()
742 ) ;
743 }
744 catch (FileNotFoundException ouch)
745 {
746 throw new FileStoreNotFoundException(
747 this.ident.toString()
748 ) ;
749 }
750 catch (IOException ouch)
751 {
752 throw new FileStoreServiceException(
753 "Unable to open data file",
754 ouch
755 ) ;
756 }
757 }
758
759 /***
760 * Get an output stream to the container contents.
761 * @throws FileStoreServiceException If unable to open the file.
762 *
763 */
764 public OutputStream getDataOutputStream()
765 throws FileStoreServiceException
766 {
767 return getDataOutputStream(false) ;
768 }
769
770 /***
771 * Get an output stream to the container contents.
772 * @param append True if the output stream should append to the end of the file.
773 * @throws FileStoreServiceException If unable to open the file.
774 *
775 */
776 public OutputStream getDataOutputStream(boolean append)
777 throws FileStoreServiceException
778 {
779 try {
780 return new FileOutputStream(
781 this.getDataFile(),
782 append
783 ) ;
784 }
785 catch (IOException ouch)
786 {
787 throw new FileStoreServiceException(
788 "Unable to open local data file",
789 ouch
790 ) ;
791 }
792 }
793
794 /***
795 * Import our data from a URL.
796 * @param url The URL to import the data from.
797 * @throws FileStoreTransferException If unable to complete the action.
798 * @throws FileStoreServiceException If unable to open the local file.
799 *
800 */
801 public void importData(URL url)
802 throws FileStoreServiceException, FileStoreTransferException
803 {
804 try {
805
806
807 URLConnection connection = url.openConnection() ;
808
809
810 this.properties.setProperty(
811 FileProperties.TRANSFER_SOURCE_URL,
812 url.toString()
813 ) ;
814
815
816
817 if (null == this.properties.getProperty(
818 FileProperties.MIME_TYPE_PROPERTY)
819 )
820 {
821 this.properties.setProperty(
822 FileProperties.MIME_TYPE_PROPERTY,
823 connection.getContentType()
824 ) ;
825 }
826
827
828
829 if (null == this.properties.getProperty(
830 FileProperties.MIME_ENCODING_PROPERTY)
831 )
832 {
833 this.properties.setProperty(
834 FileProperties.MIME_ENCODING_PROPERTY,
835 connection.getContentEncoding()
836 ) ;
837 }
838
839
840 InputStream input = null ;
841 try {
842 input = connection.getInputStream() ;
843 this.importData(
844 input
845 ) ;
846 }
847 finally
848 {
849 try {
850 if (null != input)
851 {
852 input.close() ;
853 }
854 }
855 catch (IOException ouch)
856 {
857 log.warn("----") ;
858 log.warn("RepositoryContainerImpl.importData") ;
859 log.warn("Failed to close input stream") ;
860 log.warn("----") ;
861 }
862 }
863 }
864 catch (IOException ouch)
865 {
866 throw new FileStoreTransferException(
867 "Unable to open URL source",
868 ouch
869 ) ;
870 }
871 }
872
873 /***
874 * Import our data from an InputStream.
875 * @param stream The input stream to import the data from.
876 * @throws FileStoreTransferException If unable to complete the action.
877 * @throws FileStoreServiceException If unable to open the local file.
878 *
879 */
880 public void importData(InputStream input)
881 throws FileStoreServiceException, FileStoreTransferException
882 {
883
884
885 OutputStream output = null ;
886 try {
887 output = this.getDataOutputStream() ;
888 TransferUtil trans = new TransferUtil(
889 input,
890 output
891 ) ;
892 trans.transfer() ;
893 }
894 catch (IOException ouch)
895 {
896 throw new FileStoreTransferException(
897 "Encountered error while transferring data",
898 ouch
899 ) ;
900 }
901 finally
902 {
903
904
905 try {
906 if (null != output)
907 {
908 output.close() ;
909 }
910 }
911 catch (IOException ouch)
912 {
913 log.warn("----") ;
914 log.warn("RepositoryContainerImpl.importData") ;
915 log.warn("Failed to close output stream") ;
916 log.warn("----") ;
917 }
918
919
920 this.update() ;
921 }
922 }
923
924 /***
925 * Export our data to an OutputStream.
926 * @param stream The output stream to export the data to.
927 * @throws FileStoreTransferException If unable to complete the action.
928 * @throws FileStoreServiceException If unable to open the local file.
929 *
930 */
931 public void exportData(OutputStream output)
932 throws FileStoreServiceException, FileStoreNotFoundException, FileStoreTransferException
933 {
934 InputStream input = null ;
935 try {
936 input = this.getDataInputStream() ;
937 TransferUtil trans = new TransferUtil(
938 input,
939 output
940 ) ;
941 trans.transfer() ;
942 }
943 catch (IOException ouch)
944 {
945 throw new FileStoreTransferException(
946 "Encountered error while transferring data",
947 ouch
948 ) ;
949 }
950 finally
951 {
952
953
954 try {
955 if (null != input)
956 {
957 input.close() ;
958 }
959 }
960 catch (IOException ouch)
961 {
962 log.warn("----") ;
963 log.warn("RepositoryContainerImpl.exportData") ;
964 log.warn("Failed to close input stream") ;
965 log.warn("----") ;
966 }
967 }
968 }
969 }
970 }