1 24 package org.archive.crawler.datamodel; 25 26 import java.io.IOException ; 27 import java.io.ObjectInputStream ; 28 import java.io.ObjectOutputStream ; 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.HashSet ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.Set ; 35 import java.util.concurrent.CopyOnWriteArrayList ; 36 37 import org.apache.commons.httpclient.HttpStatus; 38 import org.apache.commons.httpclient.URIException; 39 import org.archive.crawler.datamodel.credential.CredentialAvatar; 40 import org.archive.crawler.datamodel.credential.Rfc2617Credential; 41 import org.archive.crawler.extractor.Link; 42 import org.archive.crawler.framework.Processor; 43 import org.archive.crawler.framework.ProcessorChain; 44 import org.archive.crawler.util.Transform; 45 import org.archive.net.UURI; 46 import org.archive.net.UURIFactory; 47 import org.archive.util.Base32; 48 import org.archive.util.HttpRecorder; 49 50 import st.ata.util.AList; 51 import st.ata.util.HashtableAList; 52 53 54 66 public class CrawlURI extends CandidateURI 67 implements FetchStatusCodes { 68 69 private static final long serialVersionUID = 7874096757350100472L; 70 71 public static final int UNCALCULATED = -1; 72 73 80 transient private Processor nextProcessor; 82 transient private ProcessorChain nextProcessorChain; 83 private int fetchStatus = 0; private int deferrals = 0; private int fetchAttempts = 0; transient private int threadNumber; 87 88 private int linkHopCount = UNCALCULATED; private int embedHopCount = UNCALCULATED; 92 private String userAgent = null; 94 95 transient private boolean linkExtractorFinished = false; 98 99 103 public static final int MAX_OUTLINKS = Integer. 104 parseInt(System.getProperty(CrawlURI.class.getName() + ".maxOutLinks", 105 "6000")); 106 107 transient private int discardedOutlinks = 0; 108 109 private long contentSize = UNCALCULATED; 111 private long contentLength = UNCALCULATED; 112 113 118 private transient HttpRecorder httpRecorder = null; 119 120 125 private String contentType = null; 126 127 137 private boolean prerequisite = false; 138 139 142 private boolean post = false; 143 144 151 protected long ordinal; 152 153 159 private String cachedCrawlURIString = null; 160 161 166 private static final List <Object > alistPersistentMember 167 = new CopyOnWriteArrayList <Object >( 168 new String [] {A_CREDENTIAL_AVATARS_KEY}); 169 170 174 private byte[] contentDigest = null; 175 private String contentDigestScheme = null; 176 177 178 183 public CrawlURI(UURI uuri) { 184 super(uuri); 185 } 186 187 193 @SuppressWarnings ("deprecation") 194 public CrawlURI(CandidateURI caUri, long o) { 195 super(caUri.getUURI(), caUri.getPathFromSeed(), caUri.getVia(), 196 caUri.getViaContext()); 197 ordinal = o; 198 setIsSeed(caUri.isSeed()); 199 setSchedulingDirective(caUri.getSchedulingDirective()); 200 setAList(caUri.getAList()); 201 } 202 203 209 public static String fetchStatusCodesToString(int code){ 210 switch(code){ 211 case S_DNS_SUCCESS : return "DNS-1-OK"; 213 case 100 : return "HTTP-100-Info-Continue"; 215 case 101 : return "HTTP-101-Info-Switching Protocols"; 216 case 200 : return "HTTP-200-Success-OK"; 218 case 201 : return "HTTP-201-Success-Created"; 219 case 202 : return "HTTP-202-Success-Accepted"; 220 case 203 : return "HTTP-203-Success-Non-Authoritative"; 221 case 204 : return "HTTP-204-Success-No Content "; 222 case 205 : return "HTTP-205-Success-Reset Content"; 223 case 206 : return "HTTP-206-Success-Partial Content"; 224 case 300 : return "HTTP-300-Redirect-Multiple Choices"; 226 case 301 : return "HTTP-301-Redirect-Moved Permanently"; 227 case 302 : return "HTTP-302-Redirect-Found"; 228 case 303 : return "HTTP-303-Redirect-See Other"; 229 case 304 : return "HTTP-304-Redirect-Not Modified"; 230 case 305 : return "HTTP-305-Redirect-Use Proxy"; 231 case 307 : return "HTTP-307-Redirect-Temporary Redirect"; 232 case 400 : return "HTTP-400-ClientErr-Bad Request"; 234 case 401 : return "HTTP-401-ClientErr-Unauthorized"; 235 case 402 : return "HTTP-402-ClientErr-Payment Required"; 236 case 403 : return "HTTP-403-ClientErr-Forbidden"; 237 case 404 : return "HTTP-404-ClientErr-Not Found"; 238 case 405 : return "HTTP-405-ClientErr-Method Not Allowed"; 239 case 407 : return "HTTP-406-ClientErr-Not Acceptable"; 240 case 408 : return "HTTP-407-ClientErr-Proxy Authentication Required"; 241 case 409 : return "HTTP-408-ClientErr-Request Timeout"; 242 case 410 : return "HTTP-409-ClientErr-Conflict"; 243 case 406 : return "HTTP-410-ClientErr-Gone"; 244 case 411 : return "HTTP-411-ClientErr-Length Required"; 245 case 412 : return "HTTP-412-ClientErr-Precondition Failed"; 246 case 413 : return "HTTP-413-ClientErr-Request Entity Too Large"; 247 case 414 : return "HTTP-414-ClientErr-Request-URI Too Long"; 248 case 415 : return "HTTP-415-ClientErr-Unsupported Media Type"; 249 case 416 : return "HTTP-416-ClientErr-Requested Range Not Satisfiable"; 250 case 417 : return "HTTP-417-ClientErr-Expectation Failed"; 251 case 500 : return "HTTP-500-ServerErr-Internal Server Error"; 253 case 501 : return "HTTP-501-ServerErr-Not Implemented"; 254 case 502 : return "HTTP-502-ServerErr-Bad Gateway"; 255 case 503 : return "HTTP-503-ServerErr-Service Unavailable"; 256 case 504 : return "HTTP-504-ServerErr-Gateway Timeout"; 257 case 505 : return "HTTP-505-ServerErr-HTTP Version Not Supported"; 258 case S_BLOCKED_BY_USER: 260 return "Heritrix(" + S_BLOCKED_BY_USER + ")-Blocked by user"; 261 case S_BLOCKED_BY_CUSTOM_PROCESSOR: 262 return "Heritrix(" + S_BLOCKED_BY_CUSTOM_PROCESSOR + 263 ")-Blocked by custom prefetch processor"; 264 case S_DELETED_BY_USER: 265 return "Heritrix(" + S_DELETED_BY_USER + ")-Deleted by user"; 266 case S_CONNECT_FAILED: 267 return "Heritrix(" + S_CONNECT_FAILED + ")-Connection failed"; 268 case S_CONNECT_LOST: 269 return "Heritrix(" + S_CONNECT_LOST + ")-Connection lost"; 270 case S_DEEMED_CHAFF: 271 return "Heritrix(" + S_DEEMED_CHAFF + ")-Deemed chaff"; 272 case S_DEFERRED: 273 return "Heritrix(" + S_DEFERRED + ")-Deferred"; 274 case S_DOMAIN_UNRESOLVABLE: 275 return "Heritrix(" + S_DOMAIN_UNRESOLVABLE 276 + ")-Domain unresolvable"; 277 case S_OUT_OF_SCOPE: 278 return "Heritrix(" + S_OUT_OF_SCOPE + ")-Out of scope"; 279 case S_DOMAIN_PREREQUISITE_FAILURE: 280 return "Heritrix(" + S_DOMAIN_PREREQUISITE_FAILURE 281 + ")-Domain prerequisite failure"; 282 case S_ROBOTS_PREREQUISITE_FAILURE: 283 return "Heritrix(" + S_ROBOTS_PREREQUISITE_FAILURE 284 + ")-Robots prerequisite failure"; 285 case S_OTHER_PREREQUISITE_FAILURE: 286 return "Heritrix(" + S_OTHER_PREREQUISITE_FAILURE 287 + ")-Other prerequisite failure"; 288 case S_PREREQUISITE_UNSCHEDULABLE_FAILURE: 289 return "Heritrix(" + S_PREREQUISITE_UNSCHEDULABLE_FAILURE 290 + ")-Prerequisite unschedulable failure"; 291 case S_ROBOTS_PRECLUDED: 292 return "Heritrix(" + S_ROBOTS_PRECLUDED + ")-Robots precluded"; 293 case S_RUNTIME_EXCEPTION: 294 return "Heritrix(" + S_RUNTIME_EXCEPTION 295 + ")-Runtime exception"; 296 case S_SERIOUS_ERROR: 297 return "Heritrix(" + S_SERIOUS_ERROR + ")-Serious error"; 298 case S_TIMEOUT: 299 return "Heritrix(" + S_TIMEOUT + ")-Timeout"; 300 case S_TOO_MANY_EMBED_HOPS: 301 return "Heritrix(" + S_TOO_MANY_EMBED_HOPS 302 + ")-Too many embed hops"; 303 case S_TOO_MANY_LINK_HOPS: 304 return "Heritrix(" + S_TOO_MANY_LINK_HOPS 305 + ")-Too many link hops"; 306 case S_TOO_MANY_RETRIES: 307 return "Heritrix(" + S_TOO_MANY_RETRIES + ")-Too many retries"; 308 case S_UNATTEMPTED: 309 return "Heritrix(" + S_UNATTEMPTED + ")-Unattempted"; 310 case S_UNFETCHABLE_URI: 311 return "Heritrix(" + S_UNFETCHABLE_URI + ")-Unfetchable URI"; 312 case S_PROCESSING_THREAD_KILLED: 313 return "Heritrix(" + S_PROCESSING_THREAD_KILLED + ")-" + 314 "Processing thread killed"; 315 default : return Integer.toString(code); 317 } 318 } 319 320 321 327 public int getFetchStatus(){ 328 return fetchStatus; 329 } 330 331 337 public void setFetchStatus(int newstatus){ 338 fetchStatus = newstatus; 339 } 340 341 348 public int getFetchAttempts() { 349 return fetchAttempts; 350 } 351 352 359 public int incrementFetchAttempts() { 360 return fetchAttempts++; 362 } 363 364 367 public void resetFetchAttempts() { 368 this.fetchAttempts = 0; 369 } 370 371 374 public void resetDeferrals() { 375 this.deferrals = 0; 376 } 377 378 383 public Processor nextProcessor() { 384 return nextProcessor; 385 } 386 387 393 public ProcessorChain nextProcessorChain() { 394 return nextProcessorChain; 395 } 396 397 402 public void setNextProcessor(Processor processor) { 403 nextProcessor = processor; 404 } 405 406 411 public void setNextProcessorChain(ProcessorChain nextProcessorChain) { 412 this.nextProcessorChain = nextProcessorChain; 413 } 414 415 424 public void markPrerequisite(String preq, 425 ProcessorChain lastProcessorChain) throws URIException { 426 Link link = createLink(preq,Link.PREREQ_MISC,Link.PREREQ_HOP); 427 setPrerequisiteUri(link); 428 incrementDeferrals(); 429 setFetchStatus(S_DEFERRED); 430 skipToProcessorChain(lastProcessorChain); 431 } 432 433 441 public void setPrerequisiteUri(Object link) { 442 putObject(A_PREREQUISITE_URI, link); 443 } 444 445 453 public Object getPrerequisiteUri() { 454 return getObject(A_PREREQUISITE_URI); 455 } 456 457 460 public boolean hasPrerequisiteUri() { 461 return containsKey(A_PREREQUISITE_URI); 462 } 463 464 469 public boolean isPrerequisite() { 470 return this.prerequisite; 471 } 472 473 478 public void setPrerequisite(boolean prerequisite) { 479 this.prerequisite = prerequisite; 480 } 481 482 486 public String getCrawlURIString() { 487 if (this.cachedCrawlURIString == null) { 488 synchronized (this) { 489 if (this.cachedCrawlURIString == null) { 490 this.cachedCrawlURIString = 491 "CrawlURI(" + toString() + ")"; 492 } 493 } 494 } 495 return this.cachedCrawlURIString; 496 } 497 498 503 public String getContentType() { 504 return this.contentType; 505 } 506 507 512 public void setContentType(String ct) { 513 this.contentType = ct; 514 } 515 516 521 public void setThreadNumber(int i) { 522 threadNumber = i; 523 } 524 525 530 public int getThreadNumber() { 531 return threadNumber; 532 } 533 534 538 public void incrementDeferrals() { 539 deferrals++; 540 } 541 542 547 public int getDeferrals() { 548 return deferrals; 549 } 550 551 556 public void stripToMinimal() { 557 clearAList(); 558 } 559 560 565 public long getContentSize(){ 566 return contentSize; 567 } 568 569 582 public void addLocalizedError(final String processorName, 583 final Throwable ex, final String message) { 584 List <LocalizedError> localizedErrors; 585 if (containsKey(A_LOCALIZED_ERRORS)) { 586 @SuppressWarnings ("unchecked") 587 List <LocalizedError> temp = (List <LocalizedError>) getObject(A_LOCALIZED_ERRORS); 589 localizedErrors = temp; 590 } else { 591 localizedErrors = new ArrayList <LocalizedError>(); 592 putObject(A_LOCALIZED_ERRORS, localizedErrors); 593 } 594 595 localizedErrors.add(new LocalizedError(processorName, ex, message)); 596 addAnnotation("le:" + getClassSimpleName(ex.getClass()) + "@" + 597 processorName); 598 } 599 600 protected String getClassSimpleName(final Class c) { 602 String classname = c.getName(); 603 int index = classname.lastIndexOf('.'); 604 return ((index > 0 && (index + 1) < classname.length())? 605 classname.substring(index + 1): classname); 606 } 607 608 616 public void addAnnotation(String annotation) { 617 String annotations; 618 if(containsKey(A_ANNOTATIONS)) { 619 annotations = getString(A_ANNOTATIONS); 620 annotations += ","+annotation; 621 } else { 622 annotations = annotation; 623 } 624 625 putString(A_ANNOTATIONS,annotations); 626 } 627 628 633 public boolean isTruncatedFetch() { 634 return annotationContains(TRUNC_SUFFIX); 635 } 636 637 public boolean isLengthTruncatedFetch() { 638 return annotationContains(LENGTH_TRUNC); 639 } 640 641 public boolean isTimeTruncatedFetch() { 642 return annotationContains(TIMER_TRUNC); 643 } 644 645 public boolean isHeaderTruncatedFetch() { 646 return annotationContains(HEADER_TRUNC); 647 } 648 649 protected boolean annotationContains(final String str2Find) { 650 boolean result = false; 651 if (!containsKey(A_ANNOTATIONS)) { 652 return result; 653 } 654 String annotations = getString(A_ANNOTATIONS); 655 if (annotations != null && annotations.length() > 0) { 656 result = annotations.indexOf(str2Find) >= 0; 657 } 658 return result; 659 } 660 661 666 public String getAnnotations() { 667 return (containsKey(A_ANNOTATIONS))? 668 getString(A_ANNOTATIONS): null; 669 } 670 671 676 public int getEmbedHopCount() { 677 return embedHopCount; 678 } 679 680 685 public int getLinkHopCount() { 686 return linkHopCount; 687 } 688 689 693 public void markAsSeed() { 694 linkHopCount = 0; 695 embedHopCount = 0; 696 } 697 698 705 public String getUserAgent() { 706 return userAgent; 707 } 708 709 716 public void setUserAgent(String string) { 717 userAgent = string; 718 } 719 720 727 public void skipToProcessor(ProcessorChain processorChain, 728 Processor processor) { 729 setNextProcessorChain(processorChain); 730 setNextProcessor(processor); 731 } 732 733 738 public void skipToProcessorChain(ProcessorChain processorChain) { 739 setNextProcessorChain(processorChain); 740 setNextProcessor(null); 741 } 742 743 748 public long getContentLength() { 749 if (this.contentLength < 0) { 750 this.contentLength = (getHttpRecorder() != null)? 751 getHttpRecorder().getResponseContentLength(): 0; 752 } 753 return this.contentLength; 754 } 755 756 759 public void setContentSize(long l) { 760 contentSize = l; 761 } 762 763 783 public boolean hasBeenLinkExtracted(){ 784 return linkExtractorFinished; 785 } 786 787 796 public void linkExtractorFinished() { 797 linkExtractorFinished = true; 798 if(discardedOutlinks>0) { 799 addAnnotation("dol:"+discardedOutlinks); 800 } 801 } 802 803 807 public void aboutToLog() { 808 if (fetchAttempts>1) { 809 addAnnotation(fetchAttempts+"t"); 810 } 811 } 812 813 819 public HttpRecorder getHttpRecorder() { 820 return httpRecorder; 821 } 822 823 828 public void setHttpRecorder(HttpRecorder httpRecorder) { 829 this.httpRecorder = httpRecorder; 830 } 831 832 840 public boolean isHttpTransaction() { 841 return containsKey(A_HTTP_TRANSACTION); 842 } 843 844 850 public void processingCleanup() { 851 this.httpRecorder = null; 852 this.fetchStatus = S_UNATTEMPTED; 853 this.setPrerequisite(false); 854 this.contentSize = UNCALCULATED; 855 this.contentLength = UNCALCULATED; 856 this.linkExtractorFinished = false; 858 setAList(getPersistentAList()); 860 } 861 862 @SuppressWarnings ("deprecation") 863 protected AList getPersistentAList() { 864 AList newAList = new HashtableAList(); 865 if(alistPersistentMember!=null && alistPersistentMember.size() > 0) { 867 newAList.copyKeysFrom(alistPersistentMember.iterator(), getAList()); 868 } 869 List heritableKeys = (List ) getObject(A_HERITABLE_KEYS); 871 if(heritableKeys!=null) { 872 newAList.copyKeysFrom(heritableKeys.iterator(), getAList()); 873 } 874 return newAList; 875 } 876 877 887 public static CrawlURI from(CandidateURI caUri, long ordinal) { 888 return (caUri instanceof CrawlURI)? 889 (CrawlURI)caUri: new CrawlURI(caUri, ordinal); 890 } 891 892 895 private void setCredentialAvatars(Set avatars) { 896 putObject(A_CREDENTIAL_AVATARS_KEY, avatars); 897 } 898 899 902 @SuppressWarnings ("unchecked") 903 public Set <CredentialAvatar> getCredentialAvatars() { 904 return (Set )getObject(A_CREDENTIAL_AVATARS_KEY); 905 } 906 907 910 public boolean hasCredentialAvatars() { 911 return getCredentialAvatars() != null && 912 getCredentialAvatars().size() > 0; 913 } 914 915 922 public void addCredentialAvatar(CredentialAvatar ca) { 923 Set <CredentialAvatar> avatars = getCredentialAvatars(); 924 if (avatars == null) { 925 avatars = new HashSet <CredentialAvatar>(); 926 setCredentialAvatars(avatars); 927 } 928 avatars.add(ca); 929 } 930 931 934 public void removeCredentialAvatars() { 935 if (hasCredentialAvatars()) { 936 remove(A_CREDENTIAL_AVATARS_KEY); 937 } 938 } 939 940 946 public boolean removeCredentialAvatar(CredentialAvatar ca) { 947 boolean result = false; 948 Set avatars = getCredentialAvatars(); 949 if (avatars != null && avatars.size() > 0) { 950 result = avatars.remove(ca); 951 } 952 return result; 953 } 954 955 972 public boolean isSuccess() { 973 boolean result = false; 974 int statusCode = this.fetchStatus; 975 if (statusCode == HttpStatus.SC_UNAUTHORIZED && 976 hasRfc2617CredentialAvatar()) { 977 result = false; 978 } else { 979 result = (statusCode > 0); 980 } 981 return result; 982 } 983 984 988 public boolean is2XXSuccess() { 989 return this.fetchStatus >= 200 && this.fetchStatus < 300; 990 } 991 992 995 public boolean hasRfc2617CredentialAvatar() { 996 boolean result = false; 997 Set avatars = getCredentialAvatars(); 998 if (avatars != null && avatars.size() > 0) { 999 for (Iterator i = avatars.iterator(); i.hasNext();) { 1000 if (((CredentialAvatar)i.next()). 1001 match(Rfc2617Credential.class)) { 1002 result = true; 1003 break; 1004 } 1005 } 1006 } 1007 return result; 1008 } 1009 1010 1016 public void setPost(boolean b) { 1017 this.post = b; 1018 } 1019 1020 1029 public boolean isPost() { 1030 return this.post; 1031 } 1032 1033 1039 public void setContentDigest(byte[] digestValue) { 1040 setContentDigest("SHA1", digestValue); 1041 } 1042 1043 public void setContentDigest(final String scheme, 1044 final byte [] digestValue) { 1045 this.contentDigest = digestValue; 1046 this.contentDigestScheme = scheme; 1047 } 1048 1049 public String getContentDigestSchemeString() { 1050 return this.contentDigestScheme + ":" + getContentDigestString(); 1051 } 1052 1053 1058 public Object getContentDigest() { 1059 return contentDigest; 1060 } 1061 1062 public String getContentDigestString() { 1063 return Base32.encode(this.contentDigest); 1064 } 1065 1066 transient Object holder; 1067 transient Object holderKey; 1068 1069 1075 public void setHolder(Object obj) { 1076 holder=obj; 1077 } 1078 1079 1085 public Object getHolder() { 1086 return holder; 1087 } 1088 1089 1095 public void setHolderKey(Object obj) { 1096 holderKey=obj; 1097 } 1098 1104 public Object getHolderKey() { 1105 return holderKey; 1106 } 1107 1108 1113 public long getOrdinal() { 1114 return ordinal; 1115 } 1116 1117 1119 int holderCost = UNCALCULATED; 1120 1124 public int getHolderCost() { 1125 return holderCost; 1126 } 1127 1128 1133 public void setHolderCost(int cost) { 1134 holderCost = cost; 1135 } 1136 1137 1143 transient Collection <Object > outLinks = new HashSet <Object >(); 1144 1145 1156 public Collection <Link> getOutLinks() { 1157 return Transform.subclasses(outLinks, Link.class); 1158 } 1159 1160 1171 public Collection <CandidateURI> getOutCandidates() { 1172 return Transform.subclasses(outLinks, CandidateURI.class); 1173 } 1174 1175 1176 1182 public Collection <Object > getOutObjects() { 1183 return outLinks; 1184 } 1185 1186 1192 public void addOutLink(Link link) { 1193 if (outLinks.size() < MAX_OUTLINKS) { 1194 outLinks.add(link); 1195 } else { 1196 discardedOutlinks++; 1198 } 1199 } 1200 1201 public void clearOutlinks() { 1202 this.outLinks.clear(); 1203 } 1204 1205 1213 public void replaceOutlinks(Collection <CandidateURI> links) { 1214 clearOutlinks(); 1215 this.outLinks.addAll(links); 1216 } 1217 1218 1219 1222 public int outlinksSize() { 1223 return this.outLinks.size(); 1224 } 1225 1226 1239 public Link createLink(String url, CharSequence context, 1240 char hopType) throws URIException { 1241 return new Link(getUURI(), UURIFactory.getInstance(getUURI(), 1242 url), context, hopType); 1243 } 1244 1245 1257 public void createAndAddLink(String url, CharSequence context, 1258 char hopType) throws URIException { 1259 addOutLink(createLink(url, context, hopType)); 1260 } 1261 1262 1272 public void createAndAddLinkRelativeToBase(String url, 1273 CharSequence context, char hopType) throws URIException { 1274 addOutLink(new Link(getUURI(), UURIFactory.getInstance( 1275 getBaseURI(), url), context, hopType)); 1276 } 1277 1278 1289 public void createAndAddLinkRelativeToVia(String url, 1290 CharSequence context, char hopType) throws URIException { 1291 if(getVia()!=null) { 1292 addOutLink(new Link(getUURI(), UURIFactory.getInstance( 1293 getVia(), url), context, hopType)); 1294 } else { 1295 createAndAddLinkRelativeToBase(url,context,hopType); 1297 } 1298 } 1299 1300 1306 public void setBaseURI(String baseHref) throws URIException { 1307 putObject(A_HTML_BASE, UURIFactory.getInstance(baseHref)); 1308 } 1309 1310 1315 public UURI getBaseURI() { 1316 if (!containsKey(A_HTML_BASE)) { 1317 return getUURI(); 1318 } 1319 return (UURI)getObject(A_HTML_BASE); 1320 } 1321 1322 1327 public static void addAlistPersistentMember(Object key) { 1328 alistPersistentMember.add(key); 1329 } 1330 1331 1335 public static boolean removeAlistPersistentMember(Object key) { 1336 return alistPersistentMember.remove(key); 1337 } 1338 1339 1346 private void writeObject(ObjectOutputStream stream) throws IOException { 1347 stream.defaultWriteObject(); 1348 stream.writeObject((outLinks.isEmpty()) ? null : outLinks); 1349 } 1350 1351 1359 private void readObject(ObjectInputStream stream) throws IOException , 1360 ClassNotFoundException { 1361 stream.defaultReadObject(); 1362 @SuppressWarnings ("unchecked") 1363 HashSet <Object > ol = (HashSet <Object >) stream.readObject(); 1364 outLinks = (ol == null) ? new HashSet <Object >() : ol; 1365 } 1366 1367 1368} 1369 | Popular Tags |