1 40 package org.dspace.history; 41 42 import java.io.BufferedReader ; 43 import java.io.File ; 44 import java.io.FileWriter ; 45 import java.io.IOException ; 46 import java.io.PrintWriter ; 47 import java.io.StringReader ; 48 import java.io.StringWriter ; 49 import java.sql.Connection ; 50 import java.sql.PreparedStatement ; 51 import java.sql.ResultSet ; 52 import java.sql.SQLException ; 53 import java.sql.Timestamp ; 54 import java.text.NumberFormat ; 55 import java.util.LinkedList ; 56 import java.util.List ; 57 58 import org.apache.log4j.Logger; 59 import org.dspace.content.Bitstream; 60 import org.dspace.content.Collection; 61 import org.dspace.content.Community; 62 import org.dspace.content.DCValue; 63 import org.dspace.content.Item; 64 import org.dspace.content.ItemIterator; 65 import org.dspace.content.WorkspaceItem; 66 import org.dspace.core.ConfigurationManager; 67 import org.dspace.core.Context; 68 import org.dspace.core.Utils; 69 import org.dspace.eperson.EPerson; 70 import org.dspace.storage.rdbms.DatabaseManager; 71 import org.dspace.storage.rdbms.TableRow; 72 import org.dspace.workflow.WorkflowItem; 73 74 import com.hp.hpl.mesa.rdf.jena.mem.ModelMem; 75 import com.hp.hpl.mesa.rdf.jena.model.Model; 76 import com.hp.hpl.mesa.rdf.jena.model.Property; 77 import com.hp.hpl.mesa.rdf.jena.model.RDFException; 78 import com.hp.hpl.mesa.rdf.jena.model.Resource; 79 80 88 public class HistoryManager 89 { 90 public static final int NONE = 0; 92 93 public static final int CREATE = 1; 94 95 public static final int MODIFY = 2; 96 97 public static final int REMOVE = 3; 98 99 private static final String uriPrefixConfig = ConfigurationManager 100 .getProperty("history.uri.prefix"); 101 102 103 private static final String uriPrefix = (uriPrefixConfig != null) ? uriPrefixConfig 104 : "http://www.dspace.org"; 105 106 107 private static String handlePrefix = ConfigurationManager 108 .getProperty("handle.prefix"); 109 110 111 private static Logger log = Logger.getLogger(HistoryManager.class); 112 113 114 private static String historyDirectory = ConfigurationManager 115 .getProperty("history.dir"); 116 117 private static int digitsPerLevel = 2; 122 123 private static int directoryLevels = 3; 124 125 126 private static final String ID = new StringBuffer ().append( 127 HistoryManager.class.getName()).append(" ").append( 128 "$Revision: 1.11 $").toString(); 129 130 133 private HistoryManager() 134 { 135 } 136 137 151 public static void saveHistory(Context context, Object obj, int flag, 152 EPerson user, String tool) 153 { 154 try 155 { 156 createHarmonyData(context, obj, flag, user, tool); 157 } 158 catch (Exception e) 159 { 160 if (log.isDebugEnabled()) 161 { 162 log.debug("Exception while saving history", e); 163 } 164 } 165 } 166 167 181 private static void createHarmonyData(Context context, Object historyObj, 182 int flag, EPerson theUser, String theTool) throws SQLException , 183 RDFException, IOException 184 { 185 String id = (flag == REMOVE) ? getUniqueId(historyObj) : doSerialize( 186 context, historyObj); 187 188 if (theTool == null) 190 { 191 theTool = getTool(); 192 } 193 194 String eventId = getUniqueId(getShortName(historyObj), flag); 195 196 String inputStateId = (flag == CREATE) ? null : findPreviousState(id); 199 200 Model model = new ModelMem(); 202 203 Integer stateId = null; 205 TableRow row = null; 206 207 if (flag != REMOVE) 208 { 209 row = DatabaseManager.create(context, "HistoryState"); 210 stateId = new Integer (row.getIntColumn("history_state_id")); 211 } 212 213 Resource obj = model.createResource(id); 215 216 Resource event = model.createResource(eventId); 218 219 Resource outputState = (flag == REMOVE) ? null : model 221 .createResource(stateId.toString()); 222 Resource inputState = (flag == CREATE) ? null : model 223 .createResource(inputStateId); 224 225 Resource action = model.createResource(getUniqueId("action", NONE)); 227 228 Resource user = (theUser != null) ? model 230 .createResource(getUniqueId(theUser)) : null; 231 232 Property atTime = model.createProperty(getHarmonyId("atTime")); 234 Property hasInput = model.createProperty(getHarmonyId("hasInput")); 235 Property hasOutput = model.createProperty(getHarmonyId("hasOutput")); 236 Property inState = model.createProperty(getHarmonyId("inState")); 237 238 Property hasAction = model.createProperty(getHarmonyId("hasAction")); 240 Property usesTool = model.createProperty(getHarmonyId("usesTool")); 241 Property hasAgent = model.createProperty(getHarmonyId("hasAgent")); 242 Property operation = null; 243 244 if (flag == CREATE) 246 { 247 operation = model.createProperty(getHarmonyId("creates")); 248 } 249 else if (flag == REMOVE) 250 { 251 operation = model.createProperty(getHarmonyId("destroys")); 252 } 253 else if (flag == MODIFY) 254 { 255 operation = model.createProperty(getHarmonyId("transforms")); 256 } 257 else 258 { 259 throw new IllegalArgumentException ("Unknown value for flag: " 260 + flag); 261 } 262 263 if (flag != CREATE) 266 { 267 model.add(event, hasInput, inputState); 268 } 269 270 if (flag != REMOVE) 273 { 274 model.add(event, hasOutput, outputState); 275 model.add(obj, inState, outputState); 276 277 } 279 280 model.add(event, atTime, (new java.util.Date ().toString())); 282 283 model.add(action, operation, obj); 284 model.add(event, hasAction, action); 285 model.add(action, usesTool, theTool); 286 287 if (theUser != null) 288 { 289 model.add(action, hasAgent, user); 290 } 291 else 292 { 293 model.add(action, hasAgent, model.createLiteral("Unknown User")); 294 } 295 296 Property generatorId = model.createProperty(uriPrefix + "/generator"); 299 300 model.add(event, generatorId, ID); 301 302 List dbobjs = new LinkedList (); 303 304 if (flag != REMOVE) 305 { 306 row.setColumn("history_state_id", stateId.intValue()); 307 row.setColumn("object_id", id); 308 DatabaseManager.update(context, row); 309 } 310 311 StringWriter swdata = new StringWriter (); 312 313 model.write(swdata); 314 swdata.close(); 315 316 String data = swdata.toString(); 317 318 TableRow h = DatabaseManager.create(context, "History"); 319 int hid = h.getIntColumn("history_id"); 320 321 File file = forId(hid, true); 322 FileWriter fw = new FileWriter (file); 323 324 fw.write(data); 325 fw.close(); 326 327 h.setColumn("creation_date", nowAsTimeStamp()); 328 h.setColumn("checksum", Utils.getMD5(data)); 329 DatabaseManager.update(context, h); 330 } 331 332 336 343 private static String getUniqueId(Object obj) 344 { 345 if (obj == null) 346 { 347 return null; 348 } 349 350 int id = -1; 351 352 if (obj instanceof Community) 355 { 356 id = ((Community) obj).getID(); 357 } 358 else if (obj instanceof Collection) 359 { 360 id = ((Collection) obj).getID(); 361 } 362 else if (obj instanceof Item) 363 { 364 id = ((Item) obj).getID(); 365 } 366 else if (obj instanceof EPerson) 367 { 368 id = ((EPerson) obj).getID(); 369 } 370 else if (obj instanceof WorkspaceItem) 371 { 372 id = ((WorkspaceItem) obj).getID(); 373 } 374 else if (obj instanceof WorkflowItem) 375 { 376 id = ((WorkflowItem) obj).getID(); 377 } 378 379 return getUniqueIdInternal(uriPrefix, handlePrefix, getShortName(obj), 380 Integer.toString(id)); 381 } 382 383 392 private static String getUniqueId(String name, int flag) 393 { 394 String objname = new StringBuffer ("harmony").append("/").append(name) 395 .append((flag == CREATE) ? "create" : "").append( 396 (flag == MODIFY) ? "modify" : "").append( 397 (flag == REMOVE) ? "remove" : "").toString(); 398 399 return getUniqueIdInternal(uriPrefix, null, objname, Utils 400 .generateHexKey()); 401 } 402 403 412 private static String getPropertyId(String objname, String property) 413 { 414 return getUniqueIdInternal(uriPrefix, null, objname, property); 415 } 416 417 424 private static String getHarmonyId(String property) 425 { 426 return getUniqueIdInternal(uriPrefix, null, "harmony", property); 427 } 428 429 439 private static String getUniqueIdInternal(String uriPrefix, 440 String handlePrefix, String objname, String objid) 441 { 442 final String SLASH = "/"; 443 444 return new StringBuffer ().append(uriPrefix).append( 445 uriPrefix.endsWith(SLASH) ? "" : SLASH).append(objname).append( 446 objname.endsWith(SLASH) ? "" : SLASH).append( 447 (handlePrefix == null) ? "" : handlePrefix).append( 448 ((handlePrefix == null) || (handlePrefix.endsWith(SLASH))) ? "" 449 : SLASH).append(objid).toString(); 450 } 451 452 456 469 private static String serialize(Context context, Object obj) 470 throws RDFException, SQLException 471 { 472 if (obj == null) 473 { 474 return null; 475 } 476 477 Model model = new ModelMem(); 478 479 serializeInternal(context, obj, model); 481 482 StringWriter data = new StringWriter (); 483 484 model.write(data); 485 486 try 488 { 489 data.close(); 490 } 491 catch (IOException ioe) 492 { 493 } 494 495 return data.toString(); 496 } 497 498 512 private static void serializeInternal(Context context, Object obj, 513 Model model) throws RDFException, SQLException 514 { 515 if (obj == null) 516 { 517 return; 518 } 519 520 String id = getUniqueId(obj); 521 Resource res = model.createResource(id); 522 523 Property generatorId = model.createProperty(uriPrefix + "/generator"); 526 527 model.add(res, generatorId, ID); 528 529 if (obj instanceof Community) 530 { 531 addData(context, (Community) obj, res, model); 532 } 533 else if (obj instanceof Collection) 534 { 535 addData(context, (Collection) obj, res, model); 536 } 537 else if (obj instanceof Item) 538 { 539 addData(context, (Item) obj, res, model); 540 } 541 else if (obj instanceof WorkspaceItem) 542 { 543 addData(context, (WorkspaceItem) obj, res, model); 544 } 545 else if (obj instanceof WorkflowItem) 546 { 547 addData(context, (WorkflowItem) obj, res, model); 548 } 549 else if (obj instanceof EPerson) 550 { 551 addData(context, (EPerson) obj, res, model); 552 } 553 } 554 555 570 private static String doSerialize(Context context, Object obj) 571 throws SQLException , IOException , RDFException 572 { 573 if (obj == null) 574 { 575 return null; 576 } 577 578 String id = getUniqueId(obj); 579 String serialization = serialize(context, obj); 580 581 store(context, serialization); 582 583 return id; 584 } 585 586 598 private static void store(Context context, String serialization) 599 throws SQLException , IOException 600 { 601 String checksum = Utils.getMD5(serialization); 602 TableRow row = DatabaseManager.findByUnique(context, "history", 603 "checksum", checksum); 604 605 if (row != null) 607 { 608 return; 609 } 610 611 TableRow h = DatabaseManager.create(context, "History"); 612 int hid = h.getIntColumn("history_id"); 613 614 File file = forId(hid, true); 615 FileWriter fw = new FileWriter (file); 616 617 fw.write(serialization); 618 fw.close(); 619 620 h.setColumn("checksum", checksum); 621 h.setColumn("creation_date", nowAsTimeStamp()); 622 DatabaseManager.update(context, h); 623 } 624 625 633 private static String findPreviousState(String id) throws SQLException 634 { 635 Connection connection = null; 636 PreparedStatement statement = null; 637 638 try 639 { 640 String sql = "SELECT MAX(history_state_id) FROM HistoryState WHERE object_id = ?"; 641 642 connection = DatabaseManager.getConnection(); 643 statement = connection.prepareStatement(sql); 644 statement.setString(1, id); 645 646 ResultSet results = statement.executeQuery(); 647 648 return results.next() ? results.getString(1) : null; 649 } 650 finally 651 { 652 if (statement != null) 653 { 654 statement.close(); 655 } 656 657 if (connection != null) 658 { 659 connection.close(); 660 } 661 } 662 } 663 664 668 684 private static void addData(Context context, Community community, 685 Resource res, Model model) throws RDFException, SQLException 686 { 687 String shortname = getShortName(community); 688 model.add(res, model.createProperty(getPropertyId(shortname, "ID")), 689 community.getID()); 690 691 String [] metadata = new String [] { "name", "short_description", 692 "introductory_text", "copyright_text", "side_bar_text" }; 693 694 for (int i = 0; i < metadata.length; i++) 695 { 696 String meta = metadata[i]; 697 addMetadata(model, res, shortname, meta, community 698 .getMetadata(meta)); 699 } 700 701 Property hasPart = model.createProperty(getHarmonyId("hasPart")); 702 Collection[] collections = community.getCollections(); 703 704 for (int i = 0; i < collections.length; i++) 705 { 706 model.add(res, hasPart, getUniqueId(collections[i])); 707 } 708 } 709 710 726 private static void addData(Context context, Collection collection, 727 Resource res, Model model) throws SQLException , RDFException 728 { 729 String shortname = getShortName(collection); 730 731 model.add(res, model.createProperty(getPropertyId(shortname, "ID")), 732 collection.getID()); 733 model.add(res, model 734 .createProperty(getPropertyId(shortname, "license")), 735 collection.getLicense()); 736 737 String [] metadata = new String [] { "name", "short_description", 738 "introductory_text", "copyright_text", "side_bar_text", 739 "provenance_description" }; 740 741 for (int i = 0; i < metadata.length; i++) 742 { 743 String meta = metadata[i]; 744 addMetadata(model, res, shortname, meta, collection 745 .getMetadata(meta)); 746 } 747 748 Property hasPart = model.createProperty(getHarmonyId("hasPart")); 749 ItemIterator items = collection.getItems(); 750 751 while (items.hasNext()) 752 { 753 Item item = items.next(); 754 755 model.add(res, hasPart, getUniqueId(item)); 756 } 757 } 758 759 775 private static void addData(Context context, Item item, Resource res, 776 Model model) throws RDFException, SQLException 777 { 778 DCValue[] dcfields = item.getDC(Item.ANY, Item.ANY, Item.ANY); 779 780 for (int i = 0; i < dcfields.length; i++) 781 { 782 DCValue dc = dcfields[i]; 783 String element = dc.element; 784 String qualifier = dc.qualifier; 785 786 String type = new StringBuffer ().append(element).append( 787 (qualifier == null) ? "" : ".").append( 788 (qualifier == null) ? "" : qualifier).toString(); 789 790 Property p = model 791 .createProperty(uriPrefix + "/dublincore/" + type); 792 model.add(res, p, dc.value); 793 } 794 795 Property hasPart = model.createProperty(getHarmonyId("hasPart")); 799 800 Bitstream[] bitstreams = item.getNonInternalBitstreams(); 802 803 for (int i = 0; i < bitstreams.length; i++) 804 { 805 Bitstream bitstream = bitstreams[i]; 806 807 model.add(res, hasPart, getUniqueId(bitstream)); 808 809 serializeInternal(context, bitstream, model); 811 } 812 } 813 814 831 private static void addData(Context context, WorkspaceItem wi, 832 Resource res, Model model) throws SQLException , RDFException 833 { 834 Item item = Item.find(context, wi.getItem().getID()); 835 836 serializeInternal(context, item, model); 837 } 838 839 856 private static void addData(Context context, WorkflowItem wi, Resource res, 857 Model model) throws SQLException , RDFException 858 { 859 Item item = Item.find(context, wi.getItem().getID()); 860 861 serializeInternal(context, item, model); 862 } 863 864 881 private static void addData(Context context, EPerson eperson, Resource res, 882 Model model) throws SQLException , RDFException 883 { 884 String shortname = getShortName(eperson); 885 model.add(res, model.createProperty(getPropertyId(shortname, "ID")), 886 eperson.getID()); 887 model.add(res, model.createProperty(getPropertyId(shortname, "email")), 888 eperson.getEmail()); 889 model.add(res, model.createProperty(getPropertyId(shortname, 890 "firstname")), eperson.getFirstName()); 891 model.add(res, model 892 .createProperty(getPropertyId(shortname, "lastname")), eperson 893 .getLastName()); 894 model.add(res, 895 model.createProperty(getPropertyId(shortname, "active")), 896 eperson.canLogIn()); 897 model.add(res, model.createProperty(getPropertyId(shortname, 898 "require_certificate")), eperson.getRequireCertificate()); 899 900 String [] metadata = new String [] { "phone" }; 901 902 for (int i = 0; i < metadata.length; i++) 903 { 904 String meta = metadata[i]; 905 addMetadata(model, res, shortname, meta, eperson.getMetadata(meta)); 906 } 907 } 908 909 925 private static void addMetadata(Model model, Resource res, String object, 926 String property, String value) throws RDFException 927 { 928 if (value == null) 929 { 930 return; 931 } 932 933 model.add(res, model.createProperty(getPropertyId(object, property)), 934 value); 935 } 936 937 941 954 private static File forId(int id, boolean create) throws IOException 955 { 956 File file = new File (id2Filename(id)); 957 958 if (!file.exists()) 959 { 960 if (!create) 961 { 962 return null; 963 } 964 965 File parent = file.getParentFile(); 966 967 if (!parent.exists()) 968 { 969 parent.mkdirs(); 970 } 971 972 file.createNewFile(); 973 } 974 975 return file; 976 } 977 978 987 private static String id2Filename(int id) throws IOException 988 { 989 NumberFormat nf = NumberFormat.getInstance(); 990 991 nf.setGroupingUsed(false); 993 994 nf.setMinimumIntegerDigits(8); 996 997 String ID = nf.format(new Integer (id)); 998 999 StringBuffer result = new StringBuffer ().append(historyDirectory); 1001 1002 for (int i = 0; i < directoryLevels; i++) 1004 { 1005 int digits = i * digitsPerLevel; 1006 1007 result.append(File.separator).append( 1008 ID.substring(digits, digits + digitsPerLevel)); 1009 } 1010 1011 String theName = result.append(File.separator).append(id).toString(); 1013 1014 if (log.isDebugEnabled()) 1015 { 1016 log.debug("Filename for " + id + " is " + theName); 1017 } 1018 1019 return theName; 1020 } 1021 1022 1026 1031 private static String getTool() 1032 { 1033 String stacktrace = getStackTrace(); 1034 1035 try 1037 { 1038 BufferedReader reader = new BufferedReader (new StringReader ( 1039 stacktrace)); 1040 String line = null; 1041 String match = null; 1042 1043 while ((line = reader.readLine()) != null) 1044 { 1045 if (line.indexOf("org.dspace") != -1) 1046 { 1047 match = line.trim(); 1048 } 1049 } 1050 1051 return (match == null) ? stacktrace : match; 1053 } 1054 catch (Exception e) 1056 { 1057 return stacktrace; 1058 } 1059 } 1060 1061 1066 public static String getStackTrace() 1067 { 1068 StringWriter writer = new StringWriter (); 1069 1070 new Throwable ().printStackTrace(new PrintWriter (writer)); 1071 1072 return writer.toString(); 1073 } 1074 1075 1082 private static String getShortName(Object obj) 1083 { 1084 if (obj == null) 1085 { 1086 return null; 1087 } 1088 1089 String classname = obj.getClass().getName(); 1090 int index = classname.lastIndexOf("."); 1091 1092 return (index == -1) ? classname : classname.substring(index + 1); 1093 } 1094 1095 1100 private static Timestamp nowAsTimeStamp() 1101 { 1102 return new java.sql.Timestamp (new java.util.Date ().getTime()); 1103 } 1104 1105 1109 1115 public static void main(String [] argv) 1116 { 1117 Context context = null; 1118 1119 try 1120 { 1121 context = new Context(); 1122 1123 Community c = Community.find(context, 1); 1124 1125 if (c != null) 1126 { 1127 System.out.println("Got unique id " + getUniqueId(c)); 1128 1129 saveHistory(context, c, CREATE, null, null); 1130 } 1131 1132 Collection collection = Collection.find(context, 3); 1133 1134 if (collection != null) 1135 { 1136 System.out.println("Got unique id " + getUniqueId(collection)); 1137 1138 saveHistory(context, collection, CREATE, null, null); 1139 } 1140 1141 String email = "historytestuser@HistoryManager"; 1143 EPerson nep = EPerson.findByEmail(context, email); 1144 1145 if (nep == null) 1146 { 1147 nep = EPerson.create(context); 1148 } 1149 1150 nep.setFirstName("History"); 1151 nep.setEmail(email); 1152 nep.update(); 1153 1154 saveHistory(context, nep, CREATE, null, null); 1155 1156 nep.setFirstName("History Test"); 1158 nep.setLastName("User"); 1159 nep.update(); 1160 1161 saveHistory(context, nep, MODIFY, null, null); 1162 1163 Item item = Item.find(context, 1); 1164 1165 if (item != null) 1166 { 1167 saveHistory(context, item, CREATE, null, null); 1168 } 1169 1170 System.exit(0); 1171 } 1172 catch (Exception e) 1173 { 1174 e.printStackTrace(); 1175 } 1176 finally 1177 { 1178 if (context != null) 1179 { 1180 context.abort(); 1181 } 1182 } 1183 } 1184} 1185 | Popular Tags |