1 40 41 package org.dspace.content.packager; 42 43 import java.io.IOException ; 44 import java.io.InputStream ; 45 import java.io.OutputStream ; 46 import java.sql.SQLException ; 47 import java.util.ArrayList ; 48 import java.util.Date ; 49 import java.util.HashMap ; 50 import java.util.Iterator ; 51 import java.util.List ; 52 import java.util.Map ; 53 import java.util.zip.ZipEntry ; 54 import java.util.zip.ZipOutputStream ; 55 56 import org.apache.log4j.Logger; 57 import org.dspace.authorize.AuthorizeException; 58 import org.dspace.authorize.AuthorizeManager; 59 import org.dspace.content.Bitstream; 60 import org.dspace.content.Bundle; 61 import org.dspace.content.DSpaceObject; 62 import org.dspace.content.Item; 63 import org.dspace.content.crosswalk.CrosswalkException; 64 import org.dspace.content.crosswalk.DisseminationCrosswalk; 65 import org.dspace.core.ConfigurationManager; 66 import org.dspace.core.Constants; 67 import org.dspace.core.Context; 68 import org.dspace.core.PluginManager; 69 import org.dspace.core.Utils; 70 import org.jdom.Namespace; 71 import org.jdom.output.Format; 72 import org.jdom.output.XMLOutputter; 73 74 import edu.harvard.hul.ois.mets.Agent; 75 import edu.harvard.hul.ois.mets.AmdSec; 76 import edu.harvard.hul.ois.mets.Checksumtype; 77 import edu.harvard.hul.ois.mets.Div; 78 import edu.harvard.hul.ois.mets.DmdSec; 79 import edu.harvard.hul.ois.mets.FLocat; 80 import edu.harvard.hul.ois.mets.FileGrp; 81 import edu.harvard.hul.ois.mets.FileSec; 82 import edu.harvard.hul.ois.mets.Fptr; 83 import edu.harvard.hul.ois.mets.Loctype; 84 import edu.harvard.hul.ois.mets.MdWrap; 85 import edu.harvard.hul.ois.mets.Mdtype; 86 import edu.harvard.hul.ois.mets.Mets; 87 import edu.harvard.hul.ois.mets.MetsHdr; 88 import edu.harvard.hul.ois.mets.Name; 89 import edu.harvard.hul.ois.mets.Role; 90 import edu.harvard.hul.ois.mets.StructMap; 91 import edu.harvard.hul.ois.mets.TechMD; 92 import edu.harvard.hul.ois.mets.Type; 93 import edu.harvard.hul.ois.mets.XmlData; 94 import edu.harvard.hul.ois.mets.helper.MetsElement; 95 import edu.harvard.hul.ois.mets.helper.MetsException; 96 import edu.harvard.hul.ois.mets.helper.MetsValidator; 97 import edu.harvard.hul.ois.mets.helper.MetsWriter; 98 import edu.harvard.hul.ois.mets.helper.PCData; 99 import edu.harvard.hul.ois.mets.helper.PreformedXML; 100 101 129 public abstract class AbstractMETSDisseminator 130 implements PackageDisseminator 131 { 132 133 private static Logger log = Logger.getLogger(AbstractMETSDisseminator.class); 134 135 136 public static final String MANIFEST_FILE = "mets.xml"; 137 138 private static XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); 140 141 private int idCounter = 1; 143 144 150 protected Map extraFiles = null; 151 152 157 protected String gensym(String prefix) 158 { 159 return prefix + "_" + String.valueOf(idCounter++); 160 } 161 162 public String getMIMEType(PackageParameters params) 163 { 164 return (params != null && params.getProperty("manifestOnly") != null) ? 165 "text/xml" : "application/zip"; 166 } 167 168 180 public void disseminate(Context context, DSpaceObject dso, 181 PackageParameters params, OutputStream pkg) 182 throws PackageValidationException, CrosswalkException, AuthorizeException, SQLException , IOException 183 { 184 if (dso.getType() == Constants.ITEM) 185 { 186 Item item = (Item)dso; 187 long lmTime = item.getLastModified().getTime(); 188 189 String unauth = (params == null) ? null : params.getProperty("unauthorized"); 191 192 if (params != null && params.getProperty("manifestOnly") != null) 193 { 194 extraFiles = null; 195 writeManifest(context, item, params, pkg); 196 } 197 else 198 { 199 extraFiles = new HashMap (); 200 ZipOutputStream zip = new ZipOutputStream (pkg); 201 zip.setComment("METS archive created by DSpace METSDisseminationCrosswalk"); 202 203 ZipEntry me = new ZipEntry (MANIFEST_FILE); 205 me.setTime(lmTime); 206 zip.putNextEntry(me); 207 writeManifest(context, item, params, zip); 208 zip.closeEntry(); 209 210 Iterator fi = extraFiles.keySet().iterator(); 212 while (fi.hasNext()) 213 { 214 String fname = (String )fi.next(); 215 ZipEntry ze = new ZipEntry (fname); 216 ze.setTime(lmTime); 217 zip.putNextEntry(ze); 218 Utils.copy((InputStream )extraFiles.get(fname), zip); 219 zip.closeEntry(); 220 } 221 222 Bundle bundles[] = item.getBundles(); 224 for (int i = 0; i < bundles.length; i++) 225 { 226 if (!PackageUtils.isMetaInfoBundle(bundles[i])) 227 { 228 if (!AuthorizeManager.authorizeActionBoolean(context, 230 bundles[i], Constants.READ)) 231 { 232 if (unauth != null && 233 (unauth.equalsIgnoreCase("skip"))) 234 { 235 log.warn("Skipping Bundle[\""+bundles[i].getName()+"\"] because you are not authorized to read it."); 236 continue; 237 } 238 else 239 throw new AuthorizeException("Not authorized to read Bundle named \""+bundles[i].getName()+"\""); 240 } 241 Bitstream[] bitstreams = bundles[i].getBitstreams(); 242 for (int k = 0; k < bitstreams.length; k++) 243 { 244 boolean auth = AuthorizeManager.authorizeActionBoolean(context, 245 bitstreams[k], Constants.READ); 246 if (auth || 247 (unauth != null && unauth.equalsIgnoreCase("zero"))) 248 { 249 ZipEntry ze = new ZipEntry ( 250 makeBitstreamName(bitstreams[k])); 251 ze.setTime(lmTime); 252 ze.setSize(auth ? bitstreams[k].getSize() : 0); 253 zip.putNextEntry(ze); 254 if (auth) 255 Utils.copy(bitstreams[k].retrieve(), zip); 256 else 257 log.warn("Adding zero-length file for Bitstream, SID="+String.valueOf(bitstreams[k].getSequenceID())+", not authorized for READ."); 258 zip.closeEntry(); 259 } 260 else if (unauth != null && 261 unauth.equalsIgnoreCase("skip")) 262 { 263 log.warn("Skipping Bitstream, SID="+String.valueOf(bitstreams[k].getSequenceID())+", not authorized for READ."); 264 } 265 else 266 { 267 throw new AuthorizeException("Not authorized to read Bitstream, SID="+String.valueOf(bitstreams[k].getSequenceID())); 268 } 269 } 270 } 271 } 272 zip.close(); 273 extraFiles = null; 274 } 275 276 } 277 else 278 throw new PackageValidationException("Can only disseminate an Item now."); 279 } 280 281 285 private String makeBitstreamName(Bitstream bitstream) 286 { 287 String base = "bitstream_"+String.valueOf(bitstream.getID()); 288 String ext[] = bitstream.getFormat().getExtensions(); 289 return (ext.length > 0) ? base+"."+ext[0] : base; 290 } 291 292 293 private void setMdType(MdWrap mdWrap, String mdtype) 296 { 297 try 298 { 299 mdWrap.setMDTYPE(Mdtype.parse(mdtype)); 300 } 301 catch (MetsException e) 302 { 303 mdWrap.setMDTYPE(Mdtype.OTHER); 304 mdWrap.setOTHERMDTYPE(mdtype); 305 } 306 } 307 308 312 private void writeManifest(Context context, Item item, 313 PackageParameters params, OutputStream out) 314 throws PackageValidationException, CrosswalkException, AuthorizeException, SQLException , IOException 315 316 { 317 try 318 { 319 Mets mets = new Mets(); 321 322 mets.setID(gensym("mets")); 324 mets.setOBJID("hdl:" + item.getHandle()); 325 mets.setLABEL("DSpace Item"); 326 mets.setPROFILE(getProfile()); 327 328 MetsHdr metsHdr = new MetsHdr(); 330 metsHdr.setCREATEDATE(new Date ()); 334 Agent agent = new Agent(); 336 agent.setROLE(Role.CUSTODIAN); 337 agent.setTYPE(Type.ORGANIZATION); 338 Name name = new Name(); 339 name.getContent() 340 .add(new PCData(ConfigurationManager 341 .getProperty("dspace.name"))); 342 agent.getContent().add(name); 343 metsHdr.getContent().add(agent); 344 mets.getContent().add(metsHdr); 345 346 String dmdTypes[] = getDmdTypes(params); 350 351 String dmdGroup = gensym("dmd_group"); 353 String dmdId[] = new String [dmdTypes.length]; 354 for (int i = 0; i < dmdTypes.length; ++i) 355 { 356 dmdId[i] = gensym("dmd"); 357 XmlData xmlData = new XmlData(); 358 String xwalkName, metsName; 359 String parts[] = dmdTypes[i].split(":", 2); 360 if (parts.length > 1) 361 { 362 metsName = parts[0]; 363 xwalkName = parts[1]; 364 } 365 else 366 xwalkName = metsName = dmdTypes[i]; 367 368 DisseminationCrosswalk xwalk = (DisseminationCrosswalk) 369 PluginManager.getNamedPlugin(DisseminationCrosswalk.class, xwalkName); 370 if (xwalk == null) 371 throw new PackageValidationException("Cannot find "+dmdTypes[i]+" crosswalk plugin!"); 372 else 373 crosswalkToMets(xwalk, item, xmlData); 374 375 DmdSec dmdSec = new DmdSec(); 376 dmdSec.setID(dmdId[i]); 377 dmdSec.setGROUPID(dmdGroup); 378 MdWrap mdWrap = new MdWrap(); 379 setMdType(mdWrap, metsName); 380 mdWrap.getContent().add(xmlData); 381 dmdSec.getContent().add(mdWrap); 382 mets.getContent().add(dmdSec); 383 } 384 385 String licenseID = null; 389 try 390 { 391 AmdSec amdSec = new AmdSec(); 392 addRightsMd(context, item, amdSec); 393 if (amdSec.getContent().size() > 0) 394 { 395 licenseID = gensym("license"); 396 amdSec.setID(licenseID); 397 mets.getContent().add(amdSec); 398 } 399 } 400 catch (AuthorizeException e) 401 { 402 String unauth = (params == null) ? null : params.getProperty("unauthorized"); 403 if (!(unauth != null && unauth.equalsIgnoreCase("skip"))) 404 throw e; 405 else 406 log.warn("Skipping license metadata because of access failure: "+e.toString()); 407 } 408 409 411 FileSec fileSec = new FileSec(); 416 417 String techMdType = getTechMdType(params); 418 String parts[] = techMdType.split(":", 2); 419 String xwalkName, metsName; 420 if (parts.length > 1) 421 { 422 metsName = parts[0]; 423 xwalkName = parts[1]; 424 } 425 else 426 xwalkName = metsName = techMdType; 427 428 DisseminationCrosswalk xwalk = (DisseminationCrosswalk) 429 PluginManager.getNamedPlugin(DisseminationCrosswalk.class, xwalkName); 430 if (xwalk == null) 431 throw new PackageValidationException("Cannot find "+xwalkName+" crosswalk plugin!"); 432 433 String primaryBitstreamFileID = null; 435 436 List contentDivs = new ArrayList (); 438 439 String unauth = (params == null) ? null : params.getProperty("unauthorized"); 441 442 Bundle[] bundles = item.getBundles(); 443 for (int i = 0; i < bundles.length; i++) 444 { 445 if (PackageUtils.isMetaInfoBundle(bundles[i])) 446 continue; 447 448 if (!AuthorizeManager.authorizeActionBoolean(context, 451 bundles[i], Constants.READ)) 452 { 453 if (unauth != null && 454 (unauth.equalsIgnoreCase("skip"))) 455 continue; 456 else 457 throw new AuthorizeException("Not authorized to read Bundle named \""+bundles[i].getName()+"\""); 458 } 459 460 Bitstream[] bitstreams = bundles[i].getBitstreams(); 461 462 FileGrp fileGrp = new FileGrp(); 464 465 String bName = bundles[i].getName(); 467 if ((bName != null) && !bName.equals("")) 468 fileGrp.setUSE(bundleToFileGrp(bName)); 469 470 int primaryBitstreamID = -1; 472 boolean isContentBundle = false; 473 if ((bName != null) && bName.equals("ORIGINAL")) 474 { 475 isContentBundle = true; 476 primaryBitstreamID = bundles[i].getPrimaryBitstreamID(); 477 } 478 479 for (int bits = 0; bits < bitstreams.length; bits++) 480 { 481 boolean auth = AuthorizeManager.authorizeActionBoolean(context, 487 bitstreams[bits], Constants.READ); 488 if (!auth) 489 { 490 if (unauth != null && unauth.equalsIgnoreCase("skip")) 491 continue; 492 else if (!(unauth != null && unauth.equalsIgnoreCase("zero"))) 493 throw new AuthorizeException("Not authorized to read Bitstream, SID="+String.valueOf(bitstreams[bits].getSequenceID())); 494 } 495 496 String sid = String.valueOf(bitstreams[bits].getSequenceID()); 497 498 edu.harvard.hul.ois.mets.File file = new edu.harvard.hul.ois.mets.File(); 499 500 String xmlIDstart = "bitstream_"; 501 String fileID = xmlIDstart + sid; 502 503 file.setID(fileID); 504 505 if (bitstreams[bits].getID() == primaryBitstreamID) 507 primaryBitstreamFileID = fileID; 508 509 if (isContentBundle) 511 { 512 Div div = new Div(); 513 div.setID(gensym("div")); 514 div.setTYPE("DSpace Content Bitstream"); 515 Fptr fptr = new Fptr(); 516 fptr.setFILEID(fileID); 517 div.getContent().add(fptr); 518 contentDivs.add(div); 519 } 520 521 file.setSEQ(bitstreams[bits].getSequenceID()); 522 523 String groupID = "GROUP_" + xmlIDstart + sid; 524 525 530 if ((bundles[i].getName() != null) 531 && (bundles[i].getName().equals("THUMBNAIL") || 532 bundles[i].getName().startsWith("TEXT"))) 533 { 534 Bitstream original = findOriginalBitstream(item, 537 bitstreams[bits]); 538 539 if (original != null) 540 { 541 groupID = "GROUP_" + xmlIDstart 542 + original.getSequenceID(); 543 } 544 } 545 546 file.setGROUPID(groupID); 547 file.setMIMETYPE(bitstreams[bits].getFormat().getMIMEType()); 548 549 551 file.setSIZE(auth ? bitstreams[bits].getSize() : 0); 552 553 String csType = bitstreams[bits].getChecksumAlgorithm(); 555 String cs = bitstreams[bits].getChecksum(); 556 if (auth && cs != null && csType != null) 557 { 558 try 559 { 560 file.setCHECKSUMTYPE(Checksumtype.parse(csType)); 561 file.setCHECKSUM(cs); 562 } 563 catch (MetsException e) 564 { 565 log.warn("Cannot set bitstream checksum type="+csType+" in METS."); 566 } 567 } 568 569 FLocat flocat = new FLocat(); 571 flocat.setLOCTYPE(Loctype.URL); 572 flocat.setXlinkHref(makeBitstreamName(bitstreams[bits])); 573 574 String techID = "techMd_for_bitstream_"+bitstreams[bits].getSequenceID(); 576 AmdSec fAmdSec = new AmdSec(); 577 fAmdSec.setID(techID); 578 TechMD techMd = new TechMD(); 579 techMd.setID(gensym("tech")); 580 MdWrap mdWrap = new MdWrap(); 581 setMdType(mdWrap, metsName); 582 XmlData xmlData = new XmlData(); 583 mdWrap.getContent().add(xmlData); 584 techMd.getContent().add(mdWrap); 585 fAmdSec.getContent().add(techMd); 586 mets.getContent().add(fAmdSec); 587 crosswalkToMets(xwalk, bitstreams[bits], xmlData); 588 file.setADMID(techID); 589 590 file.getContent().add(flocat); 592 fileGrp.getContent().add(file); 593 } 594 595 fileSec.getContent().add(fileGrp); 597 } 598 599 mets.getContent().add(fileSec); 601 602 StringBuffer dmdIds = new StringBuffer (); 605 for (int i = 0; i < dmdId.length; ++i) 606 dmdIds.append(" "+dmdId[i]); 607 StructMap structMap = new StructMap(); 608 structMap.setID(gensym("struct")); 609 structMap.setTYPE("LOGICAL"); 610 structMap.setLABEL("DSpace"); 611 Div div0 = new Div(); 612 div0.setID(gensym("div")); 613 div0.setTYPE("DSpace Item"); 614 div0.setDMDID(dmdIds.substring(1)); 615 if (licenseID != null) 616 div0.setADMID(licenseID); 617 618 if (primaryBitstreamFileID != null) 620 { 621 Fptr fptr = new Fptr(); 622 fptr.setFILEID(primaryBitstreamFileID); 623 div0.getContent().add(fptr); 624 } 625 626 div0.getContent().addAll(contentDivs); 628 629 structMap.getContent().add(div0); 630 631 addStructMap(context, item, params, mets); 633 634 mets.getContent().add(structMap); 635 636 mets.validate(new MetsValidator()); 637 638 mets.write(new MetsWriter(out)); 639 } 640 catch (MetsException e) 641 { 642 throw new PackageValidationException(e); 646 } 647 } 648 649 650 661 protected static Bitstream findOriginalBitstream(Item item, Bitstream derived) 662 throws SQLException 663 { 664 Bundle[] bundles = item.getBundles(); 665 666 String originalFilename = derived.getName().substring(0, 669 derived.getName().length() - 4); 670 671 for (int i = 0; i < bundles.length; i++) 673 { 674 if ((bundles[i].getName() != null) 675 && bundles[i].getName().equals("ORIGINAL")) 676 { 677 Bitstream[] bitstreams = bundles[i].getBitstreams(); 679 680 for (int bsnum = 0; bsnum < bitstreams.length; bsnum++) 681 { 682 if (bitstreams[bsnum].getName().equals(originalFilename)) 683 { 684 return bitstreams[bsnum]; 685 } 686 } 687 } 688 } 689 690 return null; 692 } 693 694 private void crosswalkToMets(DisseminationCrosswalk xwalk, 697 DSpaceObject dso, MetsElement me) 698 throws CrosswalkException, 699 IOException , SQLException , AuthorizeException 700 { 701 String raw = xwalk.getSchemaLocation(); 703 String sloc[] = raw == null ? null : raw.split("\\s+"); 704 Namespace ns[] = xwalk.getNamespaces(); 705 for (int i = 0; i < ns.length; ++i) 706 { 707 String uri = ns[i].getURI(); 708 if (sloc != null && sloc.length > 1 && uri.equals(sloc[0])) 709 me.setSchema(ns[i].getPrefix(), uri, sloc[1]); 710 else 711 me.setSchema(ns[i].getPrefix(), uri); 712 } 713 714 PreformedXML pXML = 716 new PreformedXML( 717 xwalk.preferList() ? 718 outputter.outputString(xwalk.disseminateList(dso)) : 719 outputter.outputString(xwalk.disseminateElement(dso))); 720 me.getContent().add(pXML); 721 } 722 723 728 abstract public String getProfile(); 729 730 736 abstract public String bundleToFileGrp(String bname); 737 738 750 abstract public String [] getDmdTypes(PackageParameters params) 751 throws SQLException , IOException , AuthorizeException; 752 753 760 abstract public String getTechMdType(PackageParameters params) 761 throws SQLException , IOException , AuthorizeException; 762 763 768 abstract public void addRightsMd(Context context, Item item, AmdSec amdSec) 769 throws SQLException , IOException , AuthorizeException, MetsException; 770 771 778 abstract public void addStructMap(Context context, Item item, 779 PackageParameters params, Mets mets) 780 throws SQLException , IOException , AuthorizeException, MetsException; 781 } 782 | Popular Tags |