1 40 package org.dspace.storage.bitstore; 41 42 import java.io.File ; 43 import java.io.IOException ; 44 import java.io.InputStream ; 45 import java.security.DigestInputStream ; 46 import java.security.MessageDigest ; 47 import java.security.NoSuchAlgorithmException ; 48 import java.sql.SQLException ; 49 import java.util.ArrayList ; 50 import java.util.Iterator ; 51 import java.util.List ; 52 53 import org.apache.log4j.Logger; 54 import org.dspace.checker.BitstreamInfoDAO; 55 import org.dspace.core.ConfigurationManager; 56 import org.dspace.core.Context; 57 import org.dspace.core.Utils; 58 import org.dspace.storage.rdbms.DatabaseManager; 59 import org.dspace.storage.rdbms.TableRow; 60 61 import edu.sdsc.grid.io.FileFactory; 62 import edu.sdsc.grid.io.GeneralFile; 63 import edu.sdsc.grid.io.GeneralFileOutputStream; 64 import edu.sdsc.grid.io.local.LocalFile; 65 import edu.sdsc.grid.io.srb.SRBAccount; 66 import edu.sdsc.grid.io.srb.SRBFile; 67 import edu.sdsc.grid.io.srb.SRBFileSystem; 68 69 97 public class BitstreamStorageManager 98 { 99 100 private static Logger log = Logger.getLogger(BitstreamStorageManager.class); 101 102 119 private static GeneralFile[] assetStores; 120 121 122 private static int incoming; 123 124 private static final int digitsPerLevel = 2; 135 136 private static final int directoryLevels = 3; 137 138 141 private static final String REGISTERED_FLAG = "-R"; 142 143 144 static 145 { 146 ArrayList stores = new ArrayList (); 147 148 String sAssetstoreDir = ConfigurationManager 150 .getProperty("assetstore.dir"); 151 152 if (sAssetstoreDir != null) { 154 stores.add(sAssetstoreDir); } else if (ConfigurationManager.getProperty("srb.host") != null) { 156 stores.add(new SRBAccount( ConfigurationManager.getProperty("srb.host"), 158 ConfigurationManager.getIntProperty("srb.port"), 159 ConfigurationManager.getProperty("srb.username"), 160 ConfigurationManager.getProperty("srb.password"), 161 ConfigurationManager.getProperty("srb.homedirectory"), 162 ConfigurationManager.getProperty("srb.mdasdomainname"), 163 ConfigurationManager 164 .getProperty("srb.defaultstorageresource"), 165 ConfigurationManager.getProperty("srb.mcatzone"))); 166 } else { 167 log.error("No default assetstore"); 168 } 169 170 for (int i = 1;; i++) { sAssetstoreDir = ConfigurationManager.getProperty("assetstore.dir." 173 + i); 174 175 if (sAssetstoreDir != null) { stores.add(sAssetstoreDir); 178 } else if (ConfigurationManager.getProperty("srb.host." + i) 179 != null) { stores.add(new SRBAccount( 181 ConfigurationManager.getProperty("srb.host." + i), 182 ConfigurationManager.getIntProperty("srb.port." + i), 183 ConfigurationManager.getProperty("srb.username." + i), 184 ConfigurationManager.getProperty("srb.password." + i), 185 ConfigurationManager 186 .getProperty("srb.homedirectory." + i), 187 ConfigurationManager 188 .getProperty("srb.mdasdomainname." + i), 189 ConfigurationManager 190 .getProperty("srb.defaultstorageresource." + i), 191 ConfigurationManager.getProperty("srb.mcatzone." + i))); 192 } else { 193 break; } 195 } 196 197 assetStores = new GeneralFile[stores.size()]; 202 for (int i = 0; i < stores.size(); i++) { 203 Object o = stores.get(i); 204 if (o == null) { log.error("Problem with assetstore " + i); 206 } 207 if (o instanceof String ) { 208 assetStores[i] = new LocalFile((String ) o); 209 } else if (o instanceof SRBAccount) { 210 SRBFileSystem srbFileSystem = null; 211 try { 212 srbFileSystem = new SRBFileSystem((SRBAccount) o); 213 } catch (NullPointerException e) { 214 log.error("No SRBAccount for assetstore " + i); 215 } catch (IOException e) { 216 log.error("Problem getting SRBFileSystem for assetstore" 217 + i); 218 } 219 if (srbFileSystem == null) { 220 log.error("SRB FileSystem is null for assetstore " + i); 221 } 222 String sSRBAssetstore = null; 223 if (i == 0) { sSRBAssetstore = ConfigurationManager 225 .getProperty("srb.parentdir"); 226 } else { 227 sSRBAssetstore = ConfigurationManager 228 .getProperty("srb.parentdir." + i); 229 } 230 if (sSRBAssetstore == null) { 231 log.error("srb.parentdir is undefined for assetstore " + i); 232 } 233 assetStores[i] = new SRBFile(srbFileSystem, sSRBAssetstore); 234 } else { 235 log.error("Unexpected " + o.getClass().toString() 236 + " with assetstore " + i); 237 } 238 } 239 240 incoming = ConfigurationManager.getIntProperty("assetstore.incoming"); 242 } 243 244 281 public static int store(Context context, InputStream is) 282 throws SQLException , IOException 283 { 284 String id = Utils.generateKey(); 286 287 TableRow bitstream; 289 Context tempContext = null; 290 291 try 292 { 293 tempContext = new Context(); 294 295 bitstream = DatabaseManager.create(tempContext, "Bitstream"); 296 bitstream.setColumn("deleted", true); 297 bitstream.setColumn("internal_id", id); 298 299 304 bitstream.setColumn("store_number", incoming); 305 306 DatabaseManager.update(tempContext, bitstream); 307 308 tempContext.complete(); 309 } 310 catch (SQLException sqle) 311 { 312 if (tempContext != null) 313 { 314 tempContext.abort(); 315 } 316 317 throw sqle; 318 } 319 320 GeneralFile file = getFile(bitstream); 322 323 GeneralFile parent = file.getParentFile(); 325 326 if (!parent.exists()) 327 { 328 parent.mkdirs(); 329 } 330 331 file.createNewFile(); 333 334 GeneralFileOutputStream fos = FileFactory.newFileOutputStream(file); 335 336 DigestInputStream dis = null; 338 339 try 340 { 341 dis = new DigestInputStream (is, MessageDigest.getInstance("MD5")); 342 } 343 catch (NoSuchAlgorithmException nsae) 345 { 346 log.warn("Caught NoSuchAlgorithmException", nsae); 347 } 348 349 Utils.bufferedCopy(dis, fos); 350 fos.close(); 351 is.close(); 352 353 bitstream.setColumn("size_bytes", file.length()); 354 355 bitstream.setColumn("checksum", Utils.toHex(dis.getMessageDigest() 356 .digest())); 357 bitstream.setColumn("checksum_algorithm", "MD5"); 358 bitstream.setColumn("deleted", false); 359 DatabaseManager.update(context, bitstream); 360 361 int bitstream_id = bitstream.getIntColumn("bitstream_id"); 362 363 if (log.isDebugEnabled()) 364 { 365 log.debug("Stored bitstream " + bitstream_id + " in file " 366 + file.getAbsolutePath()); 367 } 368 369 return bitstream_id; 370 } 371 372 386 public static int register(Context context, int assetstore, 387 String bitstreamPath) throws SQLException , IOException { 388 389 String sInternalId = REGISTERED_FLAG + bitstreamPath; 391 392 TableRow bitstream; 394 Context tempContext = null; 395 396 try { 397 tempContext = new Context(); 398 399 bitstream = DatabaseManager.create(tempContext, "Bitstream"); 400 bitstream.setColumn("deleted", true); 401 bitstream.setColumn("internal_id", sInternalId); 402 bitstream.setColumn("store_number", assetstore); 403 DatabaseManager.update(tempContext, bitstream); 404 405 tempContext.complete(); 406 } catch (SQLException sqle) { 407 if (tempContext != null) { 408 tempContext.abort(); 409 } 410 throw sqle; 411 } 412 413 GeneralFile file = getFile(bitstream); 415 416 433 if (file instanceof LocalFile) 435 { 436 437 DigestInputStream dis = null; 439 try 440 { 441 dis = new DigestInputStream (FileFactory.newFileInputStream(file), 442 MessageDigest.getInstance("MD5")); 443 } 444 catch (NoSuchAlgorithmException e) 445 { 446 log.warn("Caught NoSuchAlgorithmException", e); 447 throw new IOException ("Invalid checksum algorithm"); 448 } 449 catch (IOException e) 450 { 451 log.error("File: " + file.getAbsolutePath() 452 + " to be registered cannot be opened - is it " 453 + "really there?"); 454 throw e; 455 } 456 final int BUFFER_SIZE = 1024 * 4; 457 final byte[] buffer = new byte[BUFFER_SIZE]; 458 while (true) 459 { 460 final int count = dis.read(buffer, 0, BUFFER_SIZE); 461 if (count == -1) 462 { 463 break; 464 } 465 } 466 bitstream.setColumn("checksum", Utils.toHex(dis.getMessageDigest() 467 .digest())); 468 dis.close(); 469 } 470 else if (file instanceof SRBFile) 471 { 472 if (!file.exists()) 473 { 474 log.error("File: " + file.getAbsolutePath() 475 + " is not in SRB MCAT"); 476 throw new IOException ("File is not in SRB MCAT"); 477 } 478 479 int iLastSlash = bitstreamPath.lastIndexOf('/'); 481 String sFilename = bitstreamPath.substring(iLastSlash + 1); 482 MessageDigest md = null; 483 try 484 { 485 md = MessageDigest.getInstance("MD5"); 486 } 487 catch (NoSuchAlgorithmException e) 488 { 489 log.error("Caught NoSuchAlgorithmException", e); 490 throw new IOException ("Invalid checksum algorithm"); 491 } 492 bitstream.setColumn("checksum", 493 Utils.toHex(md.digest(sFilename.getBytes()))); 494 } 495 else 496 { 497 throw new IOException ("Unrecognized file type - " 498 + "not local, not SRB"); 499 } 500 501 bitstream.setColumn("checksum_algorithm", "MD5"); 502 bitstream.setColumn("size_bytes", file.length()); 503 bitstream.setColumn("deleted", false); 504 DatabaseManager.update(context, bitstream); 505 506 int bitstream_id = bitstream.getIntColumn("bitstream_id"); 507 if (log.isDebugEnabled()) 508 { 509 log.debug("Stored bitstream " + bitstream_id + " in file " 510 + file.getAbsolutePath()); 511 } 512 return bitstream_id; 513 } 514 515 522 public static boolean isRegisteredBitstream(String internalId) { 523 if (internalId.substring(0, REGISTERED_FLAG.length()) 524 .equals(REGISTERED_FLAG)) 525 { 526 return true; 527 } 528 return false; 529 } 530 531 546 public static InputStream retrieve(Context context, int id) 547 throws SQLException , IOException 548 { 549 TableRow bitstream = DatabaseManager.find(context, "bitstream", id); 550 551 GeneralFile file = getFile(bitstream); 552 553 return (file != null) ? FileFactory.newFileInputStream(file) : null; 554 } 555 556 575 public static void delete(Context context, int id) throws SQLException 576 { 577 DatabaseManager.updateQuery(context, 578 "update Bitstream set deleted = '1' where bitstream_id = ? ", 579 id); 580 } 581 582 594 public static void cleanup(boolean deleteDbRecords) throws SQLException , IOException 595 { 596 Context context = null; 597 BitstreamInfoDAO bitstreamInfoDAO = new BitstreamInfoDAO(); 598 int commit_counter = 0; 599 600 try 601 { 602 context = new Context(); 603 604 String myQuery = "select * from Bitstream where deleted = '1'"; 605 606 List storage = DatabaseManager.queryTable(context, "Bitstream", myQuery) 607 .toList(); 608 609 for (Iterator iterator = storage.iterator(); iterator.hasNext();) 610 { 611 TableRow row = (TableRow) iterator.next(); 612 int bid = row.getIntColumn("bitstream_id"); 613 614 GeneralFile file = getFile(row); 615 616 if (file == null || !file.exists()) 618 { 619 log.debug("file is null"); 620 if (deleteDbRecords) 621 { 622 log.debug("deleting record"); 623 bitstreamInfoDAO.deleteBitstreamInfoWithHistory(bid); 624 DatabaseManager.delete(context, "Bitstream", bid); 625 } 626 continue; 627 } 628 629 if (isRecent(file)) 632 { 633 log.debug("file is recent"); 634 continue; 635 } 636 637 if (deleteDbRecords) 638 { 639 log.debug("deleting db record"); 640 bitstreamInfoDAO.deleteBitstreamInfoWithHistory(bid); 641 DatabaseManager.delete(context, "Bitstream", bid); 642 } 643 644 if (isRegisteredBitstream(row.getStringColumn("internal_id"))) { 645 continue; } 647 648 boolean success = file.delete(); 649 650 if (log.isDebugEnabled()) 651 { 652 log.debug("Deleted bitstream " + bid + " (file " 653 + file.getAbsolutePath() + ") with result " 654 + success); 655 } 656 657 if( success ) 665 { 666 deleteParents(file); 667 } 668 669 commit_counter++; 674 if (commit_counter % 100 == 0) 675 { 676 context.commit(); 677 } 678 } 679 680 context.complete(); 681 } 682 catch (SQLException sqle) 686 { 687 context.abort(); 688 throw sqle; 689 } 690 catch (IOException ioe) 691 { 692 context.abort(); 693 throw ioe; 694 } 695 } 696 697 701 708 private static boolean isRecent(GeneralFile file) 709 { 710 long lastmod = file.lastModified(); 711 long now = new java.util.Date ().getTime(); 712 713 if (lastmod >= now) 714 { 715 return true; 716 } 717 718 return (now - lastmod) < (1 * 60 * 1000); 720 } 721 722 728 private synchronized static void deleteParents(GeneralFile file) 729 { 730 if (file == null ) 731 { 732 return; 733 } 734 735 GeneralFile tmp = file; 736 737 for (int i = 0; i < directoryLevels; i++) 738 { 739 740 GeneralFile directory = tmp.getParentFile(); 741 GeneralFile[] files = directory.listFiles(); 742 743 if (files.length != 0) 745 { 746 break; 747 } 748 749 directory.delete(); 750 tmp = directory; 751 } 752 } 753 754 767 private static GeneralFile getFile(TableRow bitstream) throws IOException 768 { 769 if (bitstream == null) 771 { 772 return null; 773 } 774 775 int storeNumber = bitstream.getIntColumn("store_number"); 777 778 if (storeNumber == -1) 780 { 781 storeNumber = 0; 782 } 783 784 GeneralFile assetstore = assetStores[storeNumber]; 785 786 String sInternalId = bitstream.getStringColumn("internal_id"); 789 790 String sIntermediatePath = null; 798 if (isRegisteredBitstream(sInternalId)) { 799 sInternalId = sInternalId.substring(REGISTERED_FLAG.length()); 800 sIntermediatePath = ""; 801 } else { 802 803 if (sInternalId.indexOf(File.separator) != -1) 809 sInternalId = sInternalId.substring(sInternalId.lastIndexOf(File.separator)+1); 810 811 sIntermediatePath = getIntermediatePath(sInternalId); 812 } 813 814 StringBuffer bufFilename = new StringBuffer (); 815 if (assetstore instanceof LocalFile) { 816 bufFilename.append(assetstore.getCanonicalPath()); 817 bufFilename.append(File.separator); 818 bufFilename.append(sIntermediatePath); 819 bufFilename.append(sInternalId); 820 if (log.isDebugEnabled()) { 821 log.debug("Local filename for " + sInternalId + " is " 822 + bufFilename.toString()); 823 } 824 return new LocalFile(bufFilename.toString()); 825 } 826 if (assetstore instanceof SRBFile) { 827 bufFilename.append(sIntermediatePath); 828 bufFilename.append(sInternalId); 829 if (log.isDebugEnabled()) { 830 log.debug("SRB filename for " + sInternalId + " is " 831 + ((SRBFile) assetstore).toString() 832 + bufFilename.toString()); 833 } 834 return new SRBFile((SRBFile) assetstore, bufFilename.toString()); 835 } 836 return null; 837 } 838 839 847 private static String getIntermediatePath(String iInternalId) { 848 StringBuffer buf = new StringBuffer (); 849 for (int i = 0; i < directoryLevels; i++) { 850 int digits = i * digitsPerLevel; 851 if (i > 0) { 852 buf.append(File.separator); 853 } 854 buf.append(iInternalId.substring(digits, digits 855 + digitsPerLevel)); 856 } 857 buf.append(File.separator); 858 return buf.toString(); 859 } 860 861 } 862 | Popular Tags |