1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 */ 26 27 // ServiceStoreInMemory.java: An in-memory implementation 28 // of the service store. 29 // Author: James Kempf 30 // Created On: Mon Oct 20 12:36:35 1997 31 // Last Modified By: James Kempf 32 // Last Modified On: Tue Mar 2 15:32:23 1999 33 // Update Count: 472 34 // 35 36 package com.sun.slp; 37 38 import java.util.*; 39 import java.io.*; 40 41 /** 42 * The ServiceStoreInMemory class implements the ServiceStore interface 43 * on in-memory data structures. 44 * <details of those structures here> 45 * 46 * @author James Kempf 47 */ 48 49 class ServiceStoreInMemory extends Object implements ServiceStore { 50 51 /** 52 * The BVCollector interface allows various 53 * data structures to collect stuff from the BtreeVector. 54 * 55 * @author James Kempf 56 */ 57 58 private interface BVCollector { 59 60 // Set the return value. 61 setReturn(ServiceRecordInMemory rec)62 abstract void setReturn(ServiceRecordInMemory rec); 63 64 } 65 66 /** 67 * The ParserBVCollector class implements a BtreeVector 68 * collector for the parser. 69 * 70 * @author James Kempf 71 */ 72 73 private class ParserBVCollector extends Object implements BVCollector { 74 75 Parser.ParserRecord prReturns = null; 76 private Vector scopes = null; 77 ParserBVCollector(Vector scopes)78 ParserBVCollector(Vector scopes) { 79 this.scopes = scopes; 80 81 } 82 setReturn(ServiceRecordInMemory rec)83 public void setReturn(ServiceRecordInMemory rec) { 84 85 Hashtable services = prReturns.services; 86 Hashtable signatures = prReturns.signatures; 87 ServiceURL surl = rec.getServiceURL(); 88 89 // Add if we don't already have it. 90 91 if (services.get(surl) == null) { 92 Vector s = (Vector)rec.getScopes().clone(); 93 94 DATable.filterScopes(s, scopes, false); 95 96 // Need to adjust lifetime to reflect the time to live. Don't 97 // set the lifetime if it has already expired. 98 99 long lifetime = 100 (rec.getExpirationTime() - 101 System.currentTimeMillis()) / 1000; 102 103 if (lifetime > 0) { 104 ServiceURL url = 105 new ServiceURL(surl.toString(), (int)lifetime); 106 107 services.put(surl, s); 108 109 Hashtable sig = rec.getURLSignature(); 110 111 if (sig != null) { 112 signatures.put(url, sig); 113 114 } 115 } 116 } 117 } 118 } 119 120 /** 121 * The AttributeBVCollector class implements a BtreeVector 122 * collector for the collecting attribute values by type. 123 * 124 * @author James Kempf 125 */ 126 127 private class AttributeBVCollector extends Object implements BVCollector { 128 129 private Hashtable alreadySeen = new Hashtable(); 130 // records already seen. 131 private Vector attrTags = null; // tags to match against records 132 private Hashtable ht = new Hashtable(); // for collecting attributes. 133 private Vector ret = null; // for returns. 134 AttributeBVCollector(Vector attrTags, Vector ret)135 AttributeBVCollector(Vector attrTags, Vector ret) { 136 this.attrTags = attrTags; 137 this.ret = ret; 138 139 } 140 setReturn(ServiceRecordInMemory rec)141 public void setReturn(ServiceRecordInMemory rec) { 142 143 // If we've got it already, then don't add again. 144 145 if (alreadySeen.get(rec) == null) { 146 alreadySeen.put(rec, rec); 147 148 try { 149 findMatchingAttributes(rec, attrTags, ht, ret); 150 151 } catch (ServiceLocationException ex) { 152 153 Assert.slpassert(false, 154 "ssim_attrbvc_botch", 155 new Object[] {ex.getMessage()}); 156 } 157 } 158 } 159 } 160 161 /** 162 * The ScopeBVCollector class implements a BtreeVector 163 * collector for the collecting records if scopes match. 164 * 165 * @author James Kempf 166 */ 167 168 private class ScopeBVCollector extends Object implements BVCollector { 169 170 private Hashtable alreadySeen = new Hashtable(); 171 // for those we've seen 172 private Vector records = null; // for returns. 173 private Vector scopes = null; // the scopes we're looking for 174 ScopeBVCollector(Vector records, Vector scopes)175 ScopeBVCollector(Vector records, Vector scopes) { 176 this.records = records; 177 this.scopes = scopes; 178 179 } 180 setReturn(ServiceRecordInMemory rec)181 public void setReturn(ServiceRecordInMemory rec) { 182 183 // If we've got it already, then don't add. 184 185 if (alreadySeen.get(rec) == null) { 186 alreadySeen.put(rec, rec); 187 188 if (scopes == null) { 189 records.addElement(rec); 190 191 } else { 192 193 // Check scopes. 194 195 int i; 196 Vector rscopes = rec.getScopes(); 197 int len = scopes.size(); 198 199 for (i = 0; i < len; i++) { 200 if (rscopes.contains(scopes.elementAt(i))) { 201 records.addElement(rec); 202 break; 203 204 } 205 } 206 } 207 } 208 } 209 } 210 211 /** 212 * The AllBVCollector class implements a BtreeVector 213 * collector for collecting all records. 214 * 215 * @author James Kempf 216 */ 217 218 private class AllBVCollector extends Object implements BVCollector { 219 220 private Vector records = null; // for returns. 221 AllBVCollector(Vector records)222 AllBVCollector(Vector records) { 223 this.records = records; 224 225 } 226 setReturn(ServiceRecordInMemory rec)227 public void setReturn(ServiceRecordInMemory rec) { 228 229 // If we've got it already, then don't add. 230 231 if (!records.contains(rec)) { 232 records.addElement(rec); 233 234 } 235 } 236 } 237 238 /** 239 * The List class implements a linked list for storing records 240 * in the BtreeVector structure. 241 * 242 * @author James Kempf 243 */ 244 245 private class List extends Object { 246 247 ServiceRecordInMemory record = null; 248 List next = null; 249 List prev = null; 250 251 // Create a new list object. 252 List(ServiceRecordInMemory record)253 List(ServiceRecordInMemory record) { 254 this.record = record; 255 256 } 257 258 // Insert a new record after this one. Return the new 259 // record. 260 insertAfter(ServiceRecordInMemory record)261 synchronized List insertAfter(ServiceRecordInMemory record) { 262 List newRec = new List(record); 263 newRec.next = next; 264 newRec.prev = this; 265 266 if (next != null) { 267 next.prev = newRec; 268 269 } 270 271 this.next = newRec; 272 273 return newRec; 274 275 } 276 277 // Delete this record from the list. 278 delete()279 synchronized void delete() { 280 281 if (next != null) { 282 next.prev = prev; 283 } 284 285 if (prev != null) { 286 prev.next = next; 287 288 } 289 290 prev = null; 291 next = null; 292 293 } 294 } 295 296 /** 297 * The RegRecord class implements a record with the value for the 298 * record buckets. It is used as elements in BtreeVector. 299 * 300 * @author James Kempf 301 */ 302 303 private class RegRecord extends Object { 304 305 Object value = null; // the value for these registrations. 306 List head = new List(null); // head of the list always null, 307 // never changes. 308 // Construct a new one. 309 RegRecord(Object value)310 RegRecord(Object value) { 311 this.value = value; 312 313 } 314 315 // Add a new record to the buckets, return new element. 316 add(ServiceRecordInMemory rec)317 List add(ServiceRecordInMemory rec) { 318 319 return head.insertAfter(rec); 320 321 } 322 323 // For every element in record's list, set the return value in the 324 // returns object. Since deletions may have removed everything 325 // from this record, return true only if something was there. 326 setReturn(BVCollector returns)327 boolean setReturn(BVCollector returns) { 328 329 boolean match = false; 330 List l = head; 331 332 for (l = l.next; l != null; l = l.next) { 333 ServiceRecordInMemory rec = l.record; 334 returns.setReturn(rec); 335 match = true; 336 337 } 338 339 return match; 340 } 341 toString()342 public String toString() { 343 return "<RegRecord value="+value+"list="+head.next+">"; 344 345 } 346 } 347 348 /** 349 * The BtreeVector class stores registrations in sorted order. The 350 * Quicksort algorithm is used to insert items and search for something. 351 * 352 * @author James Kempf 353 */ 354 355 private class BtreeVector extends Object { 356 357 // Contains the sorted vector. 358 359 private Vector contents = new Vector(); 360 toString()361 public String toString() { 362 return "<BtreeVector "+contents.toString()+">"; 363 364 } 365 366 // Return the contents as a sorted vector of RegRecord. 367 // Note that this doesn't return a copy, so 368 // the vector can be side-effected. 369 getContents()370 Vector getContents() { 371 return contents; 372 373 } 374 375 // Add the entire contents of the vector to the return record. 376 getAll(BVCollector returns)377 boolean getAll(BVCollector returns) { 378 379 int i, n = contents.size(); 380 boolean match = false; 381 382 for (i = 0; i < n; i++) { 383 RegRecord rec = (RegRecord)contents.elementAt(i); 384 385 match = match | rec.setReturn(returns); 386 } 387 388 return match; 389 } 390 391 // Add a new record to this vector. We also garbage collect any 392 // records that are empty. Return the list object added. 393 add(Object value, ServiceRecordInMemory record)394 List add(Object value, ServiceRecordInMemory record) { 395 RegRecord rec = walkVector(value, true); // an update... 396 397 // Add the record to this one. 398 399 return rec.add(record); 400 401 } 402 403 404 // Add only if no element in the vector matches the tag. 405 matchDoesNotContain(Object pattern, BVCollector returns)406 boolean matchDoesNotContain(Object pattern, BVCollector returns) { 407 408 // Go through the vector, putting in anything that isn't equal. 409 410 int i, n = contents.size(); 411 Vector noMatch = new Vector(); 412 boolean match = false; 413 414 for (i = 0; i < n; i++) { 415 RegRecord rec = (RegRecord)contents.elementAt(i); 416 417 if (!compareEqual(rec.value, pattern)) { 418 419 // Add to prospective returns. 420 421 noMatch.addElement(rec); 422 423 } 424 425 } 426 427 // If we got this far, there are some no matches. 428 429 n = noMatch.size(); 430 431 for (i = 0; i < n; i++) { 432 RegRecord rec = (RegRecord)noMatch.elementAt(i); 433 434 match = match | rec.setReturn(returns); 435 436 } 437 438 return match; 439 440 } 441 442 boolean matchEqual(Object pattern, BVCollector returns)443 matchEqual(Object pattern, BVCollector returns) { 444 445 boolean match = false; 446 447 // We can't walk the vector if the value is an AttributePattern, 448 // because equals doesn't apply. 449 450 if (pattern instanceof AttributePattern) { 451 int i, n = contents.size(); 452 453 for (i = 0; i < n; i++) { 454 RegRecord rec = (RegRecord)contents.elementAt(i); 455 AttributeString val = (AttributeString)rec.value; 456 AttributePattern pat = (AttributePattern)pattern; 457 458 if (pat.match(val)) { 459 match = match | rec.setReturn(returns); 460 461 } 462 } 463 } else { 464 RegRecord rec = walkVector(pattern, false); 465 // not an update... 466 467 // If nothing came back, return false. 468 469 if (rec == null) { 470 match = false; 471 472 } else { 473 474 // Otherwise set returns in the vector. 475 476 match = rec.setReturn(returns); 477 478 } 479 } 480 481 return match; 482 } 483 484 boolean matchNotEqual(Object pattern, BVCollector returns)485 matchNotEqual(Object pattern, BVCollector returns) { 486 487 // Go through the vector, putting in anything that isn't equal. 488 489 int i, n = contents.size(); 490 boolean match = false; 491 492 for (i = 0; i < n; i++) { 493 RegRecord rec = (RegRecord)contents.elementAt(i); 494 495 if (!compareEqual(rec.value, pattern)) { 496 match = match | rec.setReturn(returns); 497 498 } 499 } 500 501 return match; 502 } 503 504 boolean matchLessEqual(Object pattern, BVCollector returns)505 matchLessEqual(Object pattern, 506 BVCollector returns) { 507 508 // Go through the vector, putting in anything that is 509 // less than or equal. 510 511 int i, n = contents.size(); 512 boolean match = false; 513 514 for (i = 0; i < n; i++) { 515 RegRecord rec = (RegRecord)contents.elementAt(i); 516 517 if (!compareLessEqual(rec.value, pattern)) { 518 break; 519 520 } 521 522 match = match | rec.setReturn(returns); 523 } 524 525 return match; 526 } 527 528 boolean matchNotLessEqual(Object pattern, BVCollector returns)529 matchNotLessEqual(Object pattern, 530 BVCollector returns) { 531 // Go through the vector, putting in anything that is not 532 // less than or equal. Start at the top. 533 534 int i, n = contents.size(); 535 boolean match = false; 536 537 for (i = n - 1; i >= 0; i--) { 538 RegRecord rec = (RegRecord)contents.elementAt(i); 539 540 if (compareLessEqual(rec.value, pattern)) { 541 break; 542 543 } 544 545 match = match | rec.setReturn(returns); 546 } 547 548 return match; 549 } 550 551 boolean matchGreaterEqual(Object pattern, BVCollector returns)552 matchGreaterEqual(Object pattern, 553 BVCollector returns) { 554 // Go through the vector, putting in anything that is greater 555 // than or equal. Start at the top. 556 557 int i, n = contents.size(); 558 boolean match = false; 559 560 for (i = n - 1; i >= 0; i--) { 561 RegRecord rec = (RegRecord)contents.elementAt(i); 562 563 if (!compareGreaterEqual(rec.value, pattern)) { 564 break; 565 566 } 567 568 match = match | rec.setReturn(returns); 569 } 570 571 return match; 572 } 573 574 boolean matchNotGreaterEqual(Object pattern, BVCollector returns)575 matchNotGreaterEqual(Object pattern, 576 BVCollector returns) { 577 // Go through the vector, putting in anything that is not 578 // than or equal. 579 580 int i, n = contents.size(); 581 boolean match = false; 582 583 for (i = 0; i < n; i++) { 584 RegRecord rec = (RegRecord)contents.elementAt(i); 585 586 if (compareGreaterEqual(rec.value, pattern)) { 587 break; 588 589 } 590 591 match = match | rec.setReturn(returns); 592 } 593 594 return match; 595 } 596 597 // Binary tree walk the vector, performing the operation. Note that 598 // we use dynamic typing heavily here to get maximum code reuse. 599 600 private RegRecord walkVector(Object pattern, boolean update)601 walkVector(Object pattern, boolean update) { 602 603 // Get the starting set of indicies. 604 605 int size = contents.size(); 606 int middle = size / 2; 607 int top = size - 1; 608 int bottom = 0; 609 RegRecord rec = null; 610 611 top = (top < 0 ? 0:top); 612 613 while (size > 0) { 614 615 // Get the one at the current middle. 616 617 rec = (RegRecord)contents.elementAt(middle); 618 619 // Garbage Collection. 620 // If it was null, then delete. But only if we're 621 // inserting. We leave it alone on lookup. 622 623 if (update) { 624 if (rec.head.next == null) { 625 626 contents.removeElementAt(middle); 627 628 size = size - 1; 629 middle = bottom + (size / 2); 630 top = top - 1; 631 632 top = (top < 0 ? 0:top); 633 634 continue; 635 } 636 } 637 638 // Compare value to record, if equal, return record. 639 // code. 640 641 if (compareEqual(rec.value, pattern)) { 642 return rec; 643 644 } else if (compareLessEqual(pattern, rec.value)) { 645 646 // Recalculate index. We move left, because the value is 647 // less that the value in the vector, so an equal value 648 // must be to the left. Note that the top is not in the 649 // interval because it has already been checked and 650 // found wanting. 651 652 top = middle; 653 size = (top - bottom); 654 middle = top - (size / 2); 655 middle = (middle < 0 ? 0:middle); 656 657 if (middle == top) { 658 659 // Neither top nor middle are in the interval, 660 // so size is zero. We need to compare with bottom. 661 662 rec = null; 663 RegRecord trec = (RegRecord)contents.elementAt(bottom); 664 665 if (update) { 666 rec = new RegRecord(pattern); 667 668 // If the pattern is equal to bottom, return it. 669 // If the pattern is less than or equal to bottom, 670 // we insert it at bottom. If it is greater 671 // than or equal, we insert it at middle. 672 673 if (compareEqual(trec.value, pattern)) { 674 return trec; 675 676 } else if (compareLessEqual(pattern, trec.value)) { 677 678 // Pattern is less than bottom, so insert 679 // at bottom. 680 681 contents.insertElementAt(rec, bottom); 682 683 } else { 684 contents.insertElementAt(rec, middle); 685 686 } 687 } else { 688 689 // If it equals bottom, then return bottom rec. 690 691 if (compareEqual(trec.value, pattern)) { 692 rec = trec; 693 694 } 695 } 696 697 break; 698 699 } 700 701 } else if (compareGreaterEqual(pattern, rec.value)) { 702 703 // Recalculate index. We move right, because the value is 704 // greater that the value in the vector, so an equal 705 // value must be to the right. Note that the top is not 706 // in the interval because it has already been checked 707 // and found wanting. 708 709 bottom = middle; 710 size = (top - bottom); 711 middle = bottom + (size / 2); 712 713 if (middle == bottom) { 714 715 // Neither bottom nor middle is in the interval, 716 // so size is zero. We need to compare with top. 717 718 rec = null; 719 RegRecord trec = (RegRecord)contents.elementAt(top); 720 721 if (update) { 722 rec = new RegRecord(pattern); 723 724 // If the pattern is equal to the top, we 725 // return the top. If the pattern is greater 726 // then top, we insert it after top, else we 727 // insert it at top. 728 729 if (compareEqual(trec.value, pattern)) { 730 return trec; 731 732 } else if (compareGreaterEqual(pattern, 733 trec.value)) { 734 735 // Pattern is greater than top, so insert 736 // after top. 737 738 int i = top + 1; 739 740 if (i >= contents.size()) { 741 contents.addElement(rec); 742 743 } else { 744 contents.insertElementAt(rec, i); 745 746 } 747 } else { 748 749 // Pattern is less than top, so insert at 750 // top, causing top to move up. 751 752 contents.insertElementAt(rec, top); 753 754 } 755 } else { 756 757 // If it equals top, then return top rec. 758 759 if (compareEqual(trec.value, pattern)) { 760 rec = trec; 761 762 } 763 } 764 765 break; 766 767 } 768 } 769 } 770 771 // Take care of update where vector is empty or cleaned out. 772 773 if (update && rec == null) { 774 rec = new RegRecord(pattern); 775 776 Assert.slpassert((contents.size() == 0), 777 "ssim_btree_botch", 778 new Object[0]); 779 780 contents.addElement(rec); 781 } 782 783 return rec; 784 } 785 786 // Add any registrations that match the pattern. 787 788 boolean compareEqual(Object target, Object pattern)789 compareEqual(Object target, Object pattern) { 790 791 if (target instanceof Integer || 792 target instanceof Boolean || 793 target instanceof Opaque || 794 target instanceof Long) { 795 if (pattern.equals(target)) { 796 return true; 797 798 } 799 800 } else if (target instanceof AttributeString) { 801 802 // If the pattern is an AttributePattern instead of an 803 // AttributeString, the subclass method will get invoked. 804 805 if (((AttributeString)pattern).match( 806 (AttributeString)target)) { 807 return true; 808 809 } 810 811 } else { 812 Assert.slpassert(false, 813 "ssim_unk_qtype", 814 new Object[] {pattern.getClass().getName()}); 815 } 816 817 return false; 818 819 } 820 821 // Add any registrations that are less than or equal to the pattern. 822 823 boolean compareLessEqual(Object target, Object pattern)824 compareLessEqual(Object target, Object pattern) { 825 826 if (target instanceof Integer) { 827 if (((Integer)target).intValue() <= 828 ((Integer)pattern).intValue()) { 829 return true; 830 831 } 832 833 } else if (target instanceof AttributeString) { 834 835 if (((AttributeString)target).lessEqual( 836 (AttributeString)pattern)) { 837 return true; 838 839 } 840 841 } else if (target instanceof Long) { 842 if (((Long)target).longValue() <= 843 ((Long)pattern).longValue()) { 844 return true; 845 846 } 847 848 } else if (target instanceof Boolean || 849 target instanceof Opaque) { 850 if (target.toString().compareTo(pattern.toString()) <= 0) { 851 return true; 852 853 } 854 } else { 855 Assert.slpassert(false, 856 "ssim_unk_qtype", 857 new Object[] {target.getClass().getName()}); 858 } 859 860 return false; 861 862 } 863 864 // Add any registrations that are greater than or equal to the pattern. 865 866 boolean compareGreaterEqual(Object target, Object pattern)867 compareGreaterEqual(Object target, Object pattern) { 868 869 if (target instanceof Integer) { 870 if (((Integer)target).intValue() >= 871 ((Integer)pattern).intValue()) { 872 return true; 873 874 } 875 876 } else if (target instanceof AttributeString) { 877 878 if (((AttributeString)target).greaterEqual( 879 (AttributeString)pattern)) { 880 return true; 881 882 } 883 884 } else if (target instanceof Long) { 885 if (((Long)target).longValue() >= 886 ((Long)pattern).longValue()) { 887 return true; 888 889 } 890 891 } else if (target instanceof Boolean || 892 target instanceof Opaque) { 893 if (target.toString().compareTo(pattern.toString()) >= 0) { 894 return true; 895 896 } 897 898 } else { 899 Assert.slpassert(false, 900 "ssim_unk_qtype", 901 new Object[] {target.getClass().getName()}); 902 } 903 904 return false; 905 906 } 907 } 908 909 /** 910 * The InMemoryEvaluator evaluates queries for ServiceStoreInMemory. 911 * 912 * @author James Kempf 913 */ 914 915 private class InMemoryEvaluator implements Parser.QueryEvaluator { 916 917 private Hashtable attrLevel; // Sorted attribute table. 918 private BtreeVector attrLevelNot; // Used for universal negation. 919 private Vector inScopes; // Input scopes. 920 private ParserBVCollector returns; // For gathering results. 921 InMemoryEvaluator(Hashtable ht, BtreeVector btv, Vector nscopes)922 InMemoryEvaluator(Hashtable ht, 923 BtreeVector btv, 924 Vector nscopes) { 925 attrLevel = ht; 926 attrLevelNot = btv; 927 inScopes = nscopes; 928 returns = new ParserBVCollector(inScopes); 929 930 931 } 932 933 // Evaluate the query by matching the attribute tag and 934 // value, using the operator. If invert is true, then 935 // return records that do NOT match. 936 937 public boolean evaluate(AttributeString tag, char op, Object pattern, boolean invert, Parser.ParserRecord prReturns)938 evaluate(AttributeString tag, 939 char op, 940 Object pattern, 941 boolean invert, 942 Parser.ParserRecord prReturns) 943 throws ServiceLocationException { 944 945 boolean match = false; 946 returns.prReturns = prReturns; 947 948 // If inversion is on, then gather all from the 949 // table of registrations that do NOT have this 950 // attribute. 951 952 if (invert) { 953 match = attrLevelNot.matchDoesNotContain(tag, returns); 954 955 } 956 957 // Find the table of classes v.s. sorted value vectors. 958 959 Hashtable ttable = (Hashtable)attrLevel.get(tag); 960 961 // If attribute not present, then simply return. 962 963 if (ttable == null) { 964 965 return match; 966 967 } 968 969 // If operator is present, then return all. 970 971 if (op == Parser.PRESENT) { 972 973 // ...but only if invert isn't on. 974 975 if (!invert) { 976 977 // We use attrLevelNot to get all, because it 978 // will also pick up keywords. There are 979 // no keywords in attrLevel because keywords 980 // don't have any values. 981 982 match = attrLevelNot.matchEqual(tag, returns); 983 984 } 985 986 return match; 987 } 988 989 // We know that the type table is fully initialized with 990 // BtreeVectors for each type. 991 992 // Get the pattern's class. Pattern will not be null because 993 // the parser has checked for it and PRESENT has been 994 // filtered out above. 995 996 Class pclass = pattern.getClass(); 997 String typeKey = pclass.getName(); 998 999 // If the class is AttributePattern, then use AttributeString 1000 // instead. 1001 1002 if (pattern instanceof AttributePattern) { 1003 typeKey = pclass.getSuperclass().getName(); 1004 1005 } 1006 1007 // If invert is on, collect those whose types don't match as 1008 // well. 1009 1010 if (invert) { 1011 Enumeration en = ttable.keys(); 1012 1013 while (en.hasMoreElements()) { 1014 String key = (String)en.nextElement(); 1015 1016 // Only record if the type does NOT match. 1017 1018 if (!key.equals(typeKey)) { 1019 BtreeVector bvec = (BtreeVector)ttable.get(key); 1020 1021 match = match | bvec.getAll(returns); 1022 1023 } 1024 } 1025 } 1026 1027 // Get the sorted value vector corresponding to the value class. 1028 1029 BtreeVector bvec = (BtreeVector)ttable.get(typeKey); 1030 1031 // Do the appropriate thing for the operator. 1032 1033 switch (op) { 1034 1035 case Parser.EQUAL: 1036 1037 if (!invert) { 1038 match = bvec.matchEqual(pattern, returns); 1039 1040 } else { 1041 match = bvec.matchNotEqual(pattern, returns); 1042 1043 } 1044 break; 1045 1046 case Parser.LESS: 1047 1048 // Note that we've filtered out Opaque, Boolean, and wildcarded 1049 // strings before calling this method. 1050 1051 if (!invert) { 1052 match = bvec.matchLessEqual(pattern, returns); 1053 1054 } else { 1055 match = bvec.matchNotLessEqual(pattern, returns); 1056 1057 } 1058 break; 1059 1060 case Parser.GREATER: 1061 1062 // Note that we've filtered out Opaque and Boolean 1063 // before calling this method. 1064 1065 if (!invert) { 1066 match = bvec.matchGreaterEqual(pattern, returns); 1067 1068 } else { 1069 match = bvec.matchNotGreaterEqual(pattern, returns); 1070 1071 } 1072 break; 1073 1074 default: 1075 Assert.slpassert(false, 1076 "ssim_unk_qop", 1077 new Object[] {new Character((char)op)}); 1078 } 1079 1080 return match; 1081 } 1082 } 1083 1084 /** 1085 * The ServiceRecordInMemory class implements the 1086 * ServiceStore.ServiceRecord interface on in-memory data structures. 1087 * Each property is implemented as an instance variable. 1088 * 1089 * @author James Kempf 1090 */ 1091 1092 private class ServiceRecordInMemory extends Object 1093 implements ServiceStore.ServiceRecord { 1094 1095 private ServiceURL serviceURL = null; // the service URL 1096 private Vector attrList = null; // the attribute list 1097 private Locale locale = null; // the locale 1098 private long timeToDie = 0; // when the record should die. 1099 private Vector scopes = null; // the scopes 1100 private Hashtable urlSig = null; 1101 // URL signature block list, if any. 1102 private Hashtable attrSig = null; 1103 // Attribute signature block list, if any. 1104 1105 // Create a ServiceStoreInMemory record. 1106 ServiceRecordInMemory(ServiceURL surl, Vector alist, Vector nscopes, Locale loc, Hashtable nurlSig, Hashtable nattrSig)1107 ServiceRecordInMemory(ServiceURL surl, Vector alist, 1108 Vector nscopes, Locale loc, 1109 Hashtable nurlSig, 1110 Hashtable nattrSig) { 1111 1112 // All need to be nonnull. 1113 1114 Assert.nonNullParameter(surl, "surl"); 1115 Assert.nonNullParameter(alist, "alist"); 1116 Assert.nonNullParameter(nscopes, "nscopes"); 1117 Assert.nonNullParameter(loc, "loc"); 1118 1119 serviceURL = surl; 1120 attrList = attributeVectorToServerAttribute(alist, loc); 1121 scopes = nscopes; 1122 locale = loc; 1123 urlSig = nurlSig; 1124 attrSig = nattrSig; 1125 1126 int lifetime = serviceURL.getLifetime(); 1127 1128 timeToDie = lifetime * 1000 + System.currentTimeMillis(); 1129 } 1130 1131 /** 1132 * Return the ServiceURL for the record. 1133 * 1134 * @return The record's service URL. 1135 */ 1136 getServiceURL()1137 public final ServiceURL getServiceURL() { 1138 return serviceURL; 1139 1140 } 1141 1142 /** 1143 * Return the Vector of ServerAttribute objects for the record. 1144 * 1145 * @return Vector of ServerAttribute objects for the record. 1146 */ 1147 getAttrList()1148 public final Vector getAttrList() { 1149 return attrList; 1150 1151 } 1152 1153 /** 1154 * Return the locale of the registration. 1155 * 1156 * @return The locale of the registration. 1157 */ 1158 getLocale()1159 public final Locale getLocale() { 1160 return locale; 1161 1162 } 1163 1164 /** 1165 * Return the Vector of scopes in which the record is registered. 1166 * 1167 * @return Vector of strings with scope names. 1168 */ 1169 getScopes()1170 public final Vector getScopes() { 1171 return scopes; 1172 1173 } 1174 1175 /** 1176 * Return the expiration time for the record. This informs the 1177 * service store when the record should expire and be removed 1178 * from the table. 1179 * 1180 * @return The expiration time for the record. 1181 */ 1182 getExpirationTime()1183 public long getExpirationTime() { 1184 return timeToDie; 1185 1186 } 1187 1188 /** 1189 * Return the URL signature list. 1190 * 1191 * @return URL signature block list. 1192 */ 1193 getURLSignature()1194 public Hashtable getURLSignature() { 1195 return urlSig; 1196 1197 } 1198 1199 /** 1200 * Return the attribute signature list. 1201 * 1202 * @return Attribute signature list. 1203 */ 1204 getAttrSignature()1205 public Hashtable getAttrSignature() { 1206 return attrSig; 1207 1208 } 1209 1210 1211 // 1212 // Package-local methods. 1213 setAttrList(Vector newList)1214 final void setAttrList(Vector newList) { 1215 attrList = newList; 1216 1217 } 1218 setScopes(Vector newScopes)1219 final void setScopes(Vector newScopes) { 1220 scopes = newScopes; 1221 1222 } 1223 setURLSignature(Hashtable nauth)1224 final void setURLSignature(Hashtable nauth) { 1225 urlSig = nauth; 1226 1227 } 1228 setAttrSignature(Hashtable nauth)1229 final void setAttrSignature(Hashtable nauth) { 1230 attrSig = nauth; 1231 1232 } 1233 toString()1234 public String toString() { 1235 1236 String ret = "{"; 1237 1238 ret += 1239 serviceURL + ", " + locale + ", " + attrList + ", " + 1240 scopes + ", " + locale + ", " + urlSig + ", " + attrSig; 1241 1242 ret += "}"; 1243 1244 return ret; 1245 } 1246 1247 // Convert a vector of ServiceLocationAttribute objects to 1248 // ServerAttibutes. 1249 1250 private Vector attributeVectorToServerAttribute(Vector attrs, Locale locale)1251 attributeVectorToServerAttribute(Vector attrs, Locale locale) { 1252 int i, n = attrs.size(); 1253 Vector v = new Vector(); 1254 1255 for (i = 0; i < n; i++) { 1256 ServiceLocationAttribute attr = 1257 (ServiceLocationAttribute)attrs.elementAt(i); 1258 1259 v.addElement(new ServerAttribute(attr, locale)); 1260 } 1261 1262 return v; 1263 } 1264 1265 } 1266 1267 /** 1268 * A record for scopeTypeLangTable table, 1269 * 1270 * @author James Kempf 1271 */ 1272 1273 private class STLRecord extends Object { 1274 1275 Hashtable attrValueSort = new Hashtable(); 1276 // Table of attributes, sorted by value. 1277 BtreeVector attrSort = new BtreeVector(); // Btree of attributes. 1278 boolean isAbstract = false; 1279 // True if the record is for an abstract 1280 // type. STLRecord(boolean isAbstract)1281 STLRecord(boolean isAbstract) { 1282 this.isAbstract = isAbstract; 1283 1284 } 1285 } 1286 1287 // 1288 // ServiceStoreInMemory instance variables. 1289 // 1290 1291 // ServiceStoreInMemory maintains an invaraint that the record for a 1292 // particular URL, set of scopes, and locale is the same object 1293 // (pointer-wise) regardless of where it is inserted into the table. 1294 // So it can be compared with ==. 1295 1296 // The scopeTypeLangTable 1297 // 1298 // Keys for this table are scope/service type/lang tag. Values are 1299 // STLRecord objects. The STLRecord.attrValueSort field is a Hashtable 1300 // where all registrations *having* the attribute tag keys in the 1301 // table are contained. This table is used in queries for positive 1302 // logical expressions. The STLRecord.attrSort field is a BtreeVector 1303 // keyed by attribute. It is used for negative queries to find all 1304 // records not having a particular attribute and to find all 1305 // registrations. The STLRecord.isAbstract field tells whether the record 1306 // is for an abstract type name. 1307 // 1308 // The values in the STLRecord.attrValueSort hashtable are themselves 1309 // hashtables. These hashtables are keyed by one of the type keys below, 1310 // with the values being BtreeVector objects. The BtreeVector objects 1311 // contain sorted lists of RegRecord objects for Integer, 1312 // AttributeString, Boolean, and Opaque types. All records having 1313 // values equal to the value in the RegRecord are put into a list 1314 // on the RegRecord. There is no STLRecord.attrValueSort 1315 // hashtable for keyword attributes because they have no values. 1316 // The parser evaluator must use the STLRecord.attrSort hashtable when a 1317 // present operator is encountered (the only valid operator with a 1318 // keyword). 1319 // 1320 // The values in the STLRecord.attrSort BtreeVector are RegRecord 1321 // objects with all records having that attribute tag being on the 1322 // RegRecord list. 1323 1324 // Keys for the various types. 1325 1326 private final static String INTEGER_TYPE = "java.lang.Integer"; 1327 private final static String ATTRIBUTE_STRING_TYPE = 1328 "com.sun.slp.AttributeString"; 1329 private final static String BOOLEAN_TYPE = "java.lang.Boolean"; 1330 private final static String OPAQUE_TYPE = "com.sun.slp.Opaque"; 1331 1332 private Hashtable scopeTypeLangTable = new Hashtable(); 1333 1334 // The urlScopeLangTable 1335 // 1336 // Keys for this table are service url as a string. We don't use 1337 // the service URL itself because the hash code depends on the 1338 // current service type rather than the original, and we need 1339 // to be able to distinguish for a non-service: URL if a 1340 // registration comes in with a different service type from the 1341 // original. Values are hashtables with key being scope name, 1342 // values are hashtables with lang tag key. Ultimate values are 1343 // a vector of List objects for lists in which List.record is 1344 // inserted. This table is used to perform deletions and for 1345 // finding the attributes associated with a particular URL. 1346 1347 private Hashtable urlScopeLangTable = new Hashtable(); 1348 1349 // The sstLocales Table 1350 // 1351 // The scope/service type v.s. number of languages. Keys are 1352 // the type/scope, values are a hashtable keyed by lang tag. 1353 // Values in the lang tag table are Integer objects giving 1354 // the number of registrations for that type/scope in the 1355 // given locale. 1356 1357 private Hashtable sstLocales = new Hashtable(); 1358 1359 // A queue of records sorted according to expiration time. 1360 1361 BtreeVector ageOutQueue = new BtreeVector(); 1362 1363 // Constants that indicate whether there are any registrations. 1364 1365 private final static int NO_REGS = 0; 1366 private final static int NO_REGS_IN_LOCALE = 1; 1367 private final static int REGS_IN_LOCALE = 2; 1368 1369 // Boot time. For DAAdvert timestamps. 1370 1371 private long bootTime = SLPConfig.currentSLPTime(); 1372 1373 // 1374 // ServiceStore Interface Methods. 1375 // 1376 1377 /** 1378 * Return the time since the last stateless reboot 1379 * of the ServiceStore. 1380 * 1381 * @return A Long giving the time since the last stateless reboot, 1382 * in NTP format. 1383 */ 1384 getStateTimestamp()1385 public long getStateTimestamp() { 1386 1387 return bootTime; 1388 1389 } 1390 1391 /** 1392 * Age out all records whose time has expired. 1393 * 1394 * @param deleted A Vector for return of ServiceStore.Service records 1395 * containing deleted services. 1396 * @return The time interval until another table walk must be done, 1397 * in milliseconds. 1398 * 1399 */ 1400 ageOut(Vector deleted)1401 synchronized public long ageOut(Vector deleted) { 1402 1403 // Get the ageOut queue and remove all records whose 1404 // time has popped. 1405 1406 SLPConfig conf = SLPConfig.getSLPConfig(); 1407 boolean traceDrop = conf.traceDrop(); 1408 Vector queue = ageOutQueue.getContents(); 1409 1410 // Go through the queue, dropping records that 1411 // have expired. 1412 1413 int i; 1414 1415 for (i = 0; i < queue.size(); i++) { 1416 RegRecord qRec = (RegRecord)queue.elementAt(i); 1417 long exTime = ((Long)(qRec.value)).longValue(); 1418 long time = System.currentTimeMillis(); 1419 1420 // Break out when none expire now. 1421 1422 if (exTime > time) { 1423 break; 1424 1425 } 1426 1427 // Remove the element from the queue. 1428 1429 /* 1430 * Must decrement the index 'i' otherwise the next iteration 1431 * around the loop will miss the element immediately after 1432 * the element removed. 1433 * 1434 * WARNING: Do not use 'i' again until the loop has 1435 * iterated as it may, after decrementing, 1436 * be negative. 1437 */ 1438 queue.removeElementAt(i); 1439 i--; 1440 1441 // Deregister all on this list. We 1442 // take specific care to save the next 1443 // list element before we deregister, otherwise 1444 // it will be gone after the deregister. 1445 1446 List l = qRec.head.next; 1447 1448 while (l != null) { 1449 ServiceRecordInMemory rec = l.record; 1450 ServiceURL url = rec.getServiceURL(); 1451 Vector scopes = rec.getScopes(); 1452 Locale locale = rec.getLocale(); 1453 1454 if (traceDrop) { 1455 conf.writeLog("ssim_ageout", 1456 new Object[] { 1457 url, 1458 rec.getAttrList(), 1459 scopes, 1460 locale, 1461 rec.getURLSignature(), 1462 rec.getAttrSignature(), 1463 Long.toString(time), 1464 Long.toString(exTime)}); 1465 } 1466 1467 // Save the record for the service table, in case more 1468 // processing needed. 1469 1470 deleted.addElement(rec); 1471 1472 // Save l.next NOW before deregisterInternal() removes it! 1473 1474 l = l.next; 1475 1476 String lang = locale.getLanguage(); 1477 1478 deregisterInternal(url, scopes, lang); 1479 1480 } 1481 } 1482 1483 // Calculate the new sleep time. If there's anything in the vector, 1484 // then use element 0, because the vector is sorted by time 1485 // and that will be minimum. Otherwise, use the maximum. 1486 1487 long newSleepy = Defaults.lMaxSleepTime; 1488 1489 if (queue.size() > 0) { 1490 RegRecord rec = (RegRecord)queue.elementAt(0); 1491 1492 newSleepy = 1493 ((Long)(rec.value)).longValue() - System.currentTimeMillis(); 1494 1495 newSleepy = (newSleepy > 0 ? newSleepy:0); 1496 // it will wake right up, but 1497 // so what? 1498 1499 } 1500 1501 return newSleepy; 1502 1503 } 1504 1505 /** 1506 * Create a new registration with the given parameters. 1507 * 1508 * @param url The ServiceURL. 1509 * @param attrs The Vector of ServiceLocationAttribute objects. 1510 * @param locale The Locale. 1511 * @param scopes Vector of scopes in which this record is registered. 1512 * @param urlSig auth block Hashtable for URL signature, or null if none. 1513 * @param attrSig auth block Hashtable for URL signature, or null if none. 1514 * @return True if there is an already existing registration that 1515 * this one replaced. 1516 * @exception ServiceLocationException Thrown if any 1517 * error occurs during registration or if the table 1518 * requires a network connection that failed. This 1519 * includes timeout failures. 1520 */ 1521 1522 synchronized public boolean register(ServiceURL url, Vector attrs, Vector scopes, Locale locale, Hashtable urlSig, Hashtable attrSig)1523 register(ServiceURL url, Vector attrs, 1524 Vector scopes, Locale locale, 1525 Hashtable urlSig, Hashtable attrSig) 1526 throws ServiceLocationException { 1527 1528 boolean existing = false; 1529 1530 String lang = locale.getLanguage(); 1531 1532 // Find an existing record, in any set of scopes having this language. 1533 1534 ServiceRecordInMemory rec = findExistingRecord(url, null, lang); 1535 1536 // Deregister from existing scopes, if there is an existing record. 1537 1538 if (rec != null) { 1539 if (urlSig != null) { 1540 // Ensure that the rereg SPI set and the record's SPI set are 1541 // equivalent. We need only check the URL sigs here, since 1542 // this operation is equivalent to a dereg followed by a reg, 1543 // and dereg requires only URL auth blocks. 1544 1545 Enumeration spis = urlSig.keys(); 1546 while (spis.hasMoreElements()) { 1547 Object spi = spis.nextElement(); 1548 if (rec.urlSig.remove(spi) == null) { 1549 throw new ServiceLocationException( 1550 ServiceLocationException.AUTHENTICATION_FAILED, 1551 "not_all_spis_present", 1552 new Object[] {spi}); 1553 } 1554 } 1555 if (rec.urlSig.size() != 0) { 1556 // not all required SPIs were present in SrvReg 1557 throw new ServiceLocationException( 1558 ServiceLocationException.AUTHENTICATION_FAILED, 1559 "not_all_spis_present", 1560 new Object[] {rec.urlSig.keys()}); 1561 } 1562 } 1563 1564 deregisterInternal(url, rec.getScopes(), lang); 1565 existing = true; 1566 1567 } 1568 1569 // Create a new record to register. 1570 1571 rec = new ServiceRecordInMemory(url, attrs, scopes, 1572 locale, urlSig, attrSig); 1573 1574 // Add new registration. 1575 1576 registerInternal(rec); 1577 1578 return existing; 1579 1580 } 1581 1582 /** 1583 * Deregister a ServiceURL from the database for every locale 1584 * and every scope. There will be only one record for each URL 1585 * and locale deregistered, regardless of the number of scopes in 1586 * which the URL was registered, since the attributes will be the 1587 * same in each scope if the locale is the same. 1588 * 1589 * @param url The ServiceURL 1590 * @param scopes Vector of scopes. 1591 * @param urlSig The URL signature, if any. 1592 * @exception ServiceLocationException Thrown if the 1593 * ServiceStore does not contain the URL, or if any 1594 * error occurs during the operation, or if the table 1595 * requires a network connection that failed. This 1596 * includes timeout failures. 1597 */ 1598 1599 synchronized public void deregister(ServiceURL url, Vector scopes, Hashtable urlSig)1600 deregister(ServiceURL url, Vector scopes, Hashtable urlSig) 1601 throws ServiceLocationException { 1602 1603 // Find existing record. Any locale will do. 1604 1605 ServiceRecordInMemory oldRec = 1606 findExistingRecord(url, scopes, null); 1607 1608 // Error if none. 1609 1610 if (oldRec == null) { 1611 throw 1612 new ServiceLocationException( 1613 ServiceLocationException.INVALID_REGISTRATION, 1614 "ssim_no_rec", 1615 new Object[] {url}); 1616 1617 } 1618 1619 // verify that the dereg SPI set and the record's SPI set are 1620 // equivalent 1621 if (urlSig != null) { 1622 Enumeration spis = urlSig.keys(); 1623 while (spis.hasMoreElements()) { 1624 Object spi = spis.nextElement(); 1625 if (oldRec.urlSig.remove(spi) == null) { 1626 throw new ServiceLocationException( 1627 ServiceLocationException.AUTHENTICATION_FAILED, 1628 "not_all_spis_present", 1629 new Object[] {spi}); 1630 } 1631 } 1632 if (oldRec.urlSig.size() != 0) { 1633 // not all required SPIs were present in SrvDereg 1634 throw new ServiceLocationException( 1635 ServiceLocationException.AUTHENTICATION_FAILED, 1636 "not_all_spis_present", 1637 new Object[] {oldRec.urlSig.keys()}); 1638 } 1639 } 1640 1641 /* 1642 * Deregister the URL for all locales. Use the recorded service URL 1643 * because the one passed by the client is possibly incomplete e.g. 1644 * lacking the service type. 1645 */ 1646 1647 deregisterInternal(oldRec.getServiceURL(), scopes, null); 1648 1649 } 1650 1651 /** 1652 * Update the service registration with the new parameters, adding 1653 * attributes and updating the service URL's lifetime. 1654 * 1655 * @param url The ServiceURL. 1656 * @param attrs The Vector of ServiceLocationAttribute objects. 1657 * @param locale The Locale. 1658 * @param scopes Vector of scopes in which this record is registered. 1659 * @exception ServiceLocationException Thrown if any 1660 * error occurs during registration or if the table 1661 * requires a network connection that failed. This 1662 * includes timeout failures. 1663 */ 1664 1665 synchronized public void updateRegistration(ServiceURL url, Vector attrs, Vector scopes, Locale locale)1666 updateRegistration(ServiceURL url, Vector attrs, 1667 Vector scopes, Locale locale) 1668 throws ServiceLocationException { 1669 1670 String lang = locale.getLanguage(); 1671 ServiceRecordInMemory oldRec = 1672 findExistingRecord(url, scopes, lang); 1673 1674 // Error if none. 1675 1676 if (oldRec == null) { 1677 throw 1678 new ServiceLocationException( 1679 ServiceLocationException.INVALID_UPDATE, 1680 "ssim_no_rec", 1681 new Object[] {url}); 1682 1683 } 1684 1685 // If this is a nonServiceURL, check whether it's registered 1686 // under a different service type. 1687 1688 ServiceType type = url.getServiceType(); 1689 1690 if (!type.isServiceURL()) { 1691 checkForExistingUnderOtherServiceType(url, scopes); 1692 1693 } 1694 1695 // Deregister the URL in this locale. 1696 1697 deregisterInternal(url, scopes, lang); 1698 1699 // Create a new record to update. 1700 1701 ServiceRecordInMemory rec = 1702 new ServiceRecordInMemory(url, attrs, scopes, 1703 locale, null, null); 1704 1705 // Merge old record into new. 1706 1707 mergeOldRecordIntoNew(oldRec, rec); 1708 1709 // Add the new record. 1710 1711 registerInternal(rec); 1712 } 1713 1714 /** 1715 * Delete the attributes from the ServiceURL object's table entries. 1716 * Delete for every locale that has the attributes and every scope. 1717 * Note that the attribute tags must be lower-cased in the locale of 1718 * the registration, not in the locale of the request. 1719 * 1720 * @param url The ServiceURL. 1721 * @param scopes Vector of scopes. 1722 * @param attrTags The Vector of String 1723 * objects specifying the attribute tags of 1724 * the attributes to delete. 1725 * @param locale Locale of the request. 1726 * @exception ServiceLocationException Thrown if the 1727 * ServiceStore does not contain the URL or if any 1728 * error occurs during the operation or if the table 1729 * requires a network connection that failed. This 1730 * includes timeout failures. 1731 */ 1732 1733 synchronized public void deleteAttributes(ServiceURL url, Vector scopes, Vector attrTags, Locale locale)1734 deleteAttributes(ServiceURL url, 1735 Vector scopes, 1736 Vector attrTags, 1737 Locale locale) 1738 throws ServiceLocationException { 1739 1740 String lang = SLPConfig.localeToLangTag(locale); 1741 1742 // Get the scope level from urlScopeLangTable. 1743 1744 Hashtable scopeLevel = 1745 (Hashtable)urlScopeLangTable.get(url.toString()); 1746 1747 // Error if no old record to update. 1748 1749 if (scopeLevel == null) { 1750 throw 1751 new ServiceLocationException( 1752 ServiceLocationException.INVALID_REGISTRATION, 1753 "ssim_no_rec", 1754 new Object[] {url}); 1755 1756 } 1757 1758 // Check existing records to be sure that the scopes 1759 // match. Attributes must be the same across 1760 // scopes. 1761 1762 checkScopeStatus(url, 1763 scopes, 1764 ServiceLocationException.INVALID_REGISTRATION); 1765 1766 // Create attribute patterns for the default locale. This 1767 // is an optimization. Only Turkish differs in lower 1768 // case from the default. If there are any other exceptions, 1769 // we need to move this into the loop. 1770 1771 Vector attrPatterns = 1772 stringVectorToAttributePattern(attrTags, Defaults.locale); 1773 1774 // Look through the language table for this language at scope level. 1775 1776 Enumeration en = scopeLevel.keys(); 1777 1778 Assert.slpassert(en.hasMoreElements(), 1779 "ssim_empty_scope_table", 1780 new Object[] {url}); 1781 1782 Hashtable ht = new Hashtable(); 1783 boolean foundIt = false; 1784 1785 while (en.hasMoreElements()) { 1786 String scope = (String)en.nextElement(); 1787 Hashtable langLevel = (Hashtable)scopeLevel.get(scope); 1788 Enumeration een = langLevel.keys(); 1789 1790 Assert.slpassert(een.hasMoreElements(), 1791 "ssim_empty_lang_table", 1792 new Object[] {url}); 1793 1794 // Find the list of records for this language. 1795 1796 Vector listVec = (Vector)langLevel.get(lang); 1797 1798 if (listVec == null) { 1799 continue; 1800 1801 } 1802 1803 foundIt = true; 1804 1805 List elem = (List)listVec.elementAt(0); 1806 ServiceRecordInMemory rec = elem.record; 1807 Locale loc = rec.getLocale(); 1808 1809 // If we've done this one already, go on. 1810 1811 if (ht.get(rec) != null) { 1812 continue; 1813 1814 } 1815 1816 ht.put(rec, rec); 1817 1818 // Delete old registration. 1819 1820 deregisterInternal(url, rec.getScopes(), lang); 1821 1822 // Delete attributes from this record. 1823 1824 // If the locale is Turkish, then use the Turkish patterns. 1825 1826 if (loc.getLanguage().equals("tr")) { 1827 Vector turkishTags = 1828 stringVectorToAttributePattern(attrTags, loc); 1829 1830 deleteAttributes(rec, turkishTags); 1831 1832 } else { 1833 deleteAttributes(rec, attrPatterns); 1834 1835 } 1836 1837 // Reregister the record. 1838 1839 registerInternal(rec); 1840 } 1841 1842 // If no record found, report error. 1843 1844 if (!foundIt) { 1845 throw 1846 new ServiceLocationException( 1847 ServiceLocationException.INVALID_REGISTRATION, 1848 "ssim_no_rec_locale", 1849 new Object[] {url, locale}); 1850 1851 } 1852 1853 } 1854 1855 /** 1856 * Return a Vector of String containing the service types for this 1857 * scope and naming authority. If there are none, an empty vector is 1858 * returned. 1859 * 1860 * @param namingAuthority The namingAuthority, or "*" if for all. 1861 * @param scopes The scope names. 1862 * @return A Vector of String objects that are the type names, or 1863 * an empty vector if there are none. 1864 * @exception ServiceLocationException Thrown if any 1865 * error occurs during the operation or if the table 1866 * requires a network connection that failed. This 1867 * includes timeout failures. 1868 */ 1869 1870 synchronized public Vector findServiceTypes(String namingAuthority, Vector scopes)1871 findServiceTypes(String namingAuthority, Vector scopes) 1872 throws ServiceLocationException { 1873 1874 Vector ret = new Vector(); 1875 Enumeration keys = scopeTypeLangTable.keys(); 1876 boolean isWildCard = namingAuthority.equals("*"); 1877 boolean isIANA = (namingAuthority.length() <= 0); 1878 1879 // Get all the keys in the table, look for scope. 1880 1881 while (keys.hasMoreElements()) { 1882 String sstKey = (String)keys.nextElement(); 1883 1884 // Check whether this is an abstract type entry. 1885 // If so, then we ignore it, because we only 1886 // want full type names in the return. 1887 1888 if (isAbstractTypeRecord(sstKey)) { 1889 continue; 1890 1891 } 1892 1893 // If the scope matches then check the naming authority. 1894 1895 String keyScope = keyScope(sstKey); 1896 1897 if (scopes.contains(keyScope)) { 1898 String keyType = keyServiceType(sstKey); 1899 1900 // If not already there, see if we should add this one to the 1901 // vector. 1902 1903 if (!ret.contains(keyType)) { 1904 ServiceType type = new ServiceType(keyType); 1905 1906 // If wildcard, then simply add it to the vector. 1907 1908 if (isWildCard) { 1909 ret.addElement(type.toString()); 1910 1911 } else { 1912 1913 // Check naming authority. 1914 1915 String na = type.getNamingAuthority(); 1916 1917 if (type.isNADefault() && isIANA) { // check for IANA.. 1918 ret.addElement(type.toString()); 1919 1920 } else if (namingAuthority.equals(na)) { // Not IANA.. 1921 ret.addElement(type.toString()); 1922 1923 } 1924 } 1925 } 1926 } 1927 } 1928 1929 return ret; 1930 } 1931 1932 /** 1933 * Return a Hashtable with the key FS_SERVICES matched to the 1934 * hashtable of ServiceURL objects as key and a vector 1935 * of their scopes as value, and the key FS_SIGTABLE 1936 * matched to a hashtable with ServiceURL objects as key 1937 * and the auth block Hashtable for the URL (if any) for value. The 1938 * returned service URLs will match the service type, scope, query, 1939 * and locale. If there are no signatures, the FS_SIGTABLE 1940 * key returns null. If there are no 1941 * registrations in any locale, FS_SERVICES is bound to an 1942 * empty table. 1943 * 1944 * @param serviceType The service type name. 1945 * @param scope The scope name. 1946 * @param query The query, with any escaped characters as yet unprocessed. 1947 * @param locale The locale in which to lowercase query and search. 1948 * @return A Hashtable with the key FS_SERVICES matched to the 1949 * hashtable of ServiceURL objects as key and a vector 1950 * of their scopes as value, and the key FS_SIGTABLE 1951 * matched to a hashtable with ServiceURL objects as key 1952 * and the auth block Hashtable for the URL (if any) for value. 1953 * If there are no registrations in any locale, FS_SERVICES 1954 * is bound to an empty table. 1955 * @exception ServiceLocationException Thrown if a parse error occurs 1956 * during query parsing or if any 1957 * error occurs during the operation or if the table 1958 * requires a network connection that failed. This 1959 * includes timeout failures. 1960 */ 1961 1962 synchronized public Hashtable findServices(String serviceType, Vector scopes, String query, Locale locale)1963 findServices(String serviceType, 1964 Vector scopes, 1965 String query, 1966 Locale locale) 1967 throws ServiceLocationException { 1968 1969 String lang = locale.getLanguage(); 1970 Parser.ParserRecord ret = new Parser.ParserRecord(); 1971 Hashtable services = null; 1972 Hashtable signatures = null; 1973 int i, n = scopes.size(); 1974 int len = 0; 1975 1976 // Get the services and signatures tables. 1977 1978 services = ret.services; 1979 signatures = ret.signatures; 1980 1981 // Remove leading and trailing spaces. 1982 1983 query = query.trim(); 1984 len = query.length(); 1985 1986 // Check whether there are any registrations for this type/scope/ 1987 // language tag and, if not, whether there are others. 1988 // in another language, but not this one. 1989 1990 int regStatus = languageSupported(serviceType, scopes, lang); 1991 1992 if (regStatus == NO_REGS_IN_LOCALE) { 1993 throw 1994 new ServiceLocationException( 1995 ServiceLocationException.LANGUAGE_NOT_SUPPORTED, 1996 "ssim_lang_unsup", 1997 new Object[] {locale}); 1998 1999 } else if (regStatus == REGS_IN_LOCALE) { 2000 2001 // Only do query if regs exist. 2002 2003 for (i = 0; i < n; i++) { 2004 String scope = (String)scopes.elementAt(i); 2005 String sstKey = 2006 makeScopeTypeLangKey(scope, serviceType, lang); 2007 STLRecord regRecs = 2008 (STLRecord)scopeTypeLangTable.get(sstKey); 2009 2010 // If no record for this combo of service type and 2011 // scope, continue. 2012 2013 if (regRecs == null) { 2014 continue; 2015 } 2016 2017 // Special case if the query string is empty. This 2018 // indicates that all registrations should be returned. 2019 2020 if (len <= 0) { 2021 BtreeVector bvec = regRecs.attrSort; 2022 ParserBVCollector collector = 2023 new ParserBVCollector(scopes); 2024 collector.prReturns = ret; 2025 2026 // Use the BtreeVector.getAll() method to get all 2027 // registrations. We will end up revisiting some 2028 // list elements because there will be ones 2029 // for multiple attributes, but that will be 2030 // filtered in the BVCollector.setReturn() method. 2031 2032 bvec.getAll(collector); 2033 2034 } else { 2035 2036 // Otherwise, use the LDAPv3 parser to evaluate. 2037 2038 InMemoryEvaluator ev = 2039 new InMemoryEvaluator(regRecs.attrValueSort, 2040 regRecs.attrSort, 2041 scopes); 2042 2043 Parser.parseAndEvaluateQuery(query, ev, locale, ret); 2044 2045 } 2046 } 2047 } 2048 2049 // Create return hashtable. 2050 2051 Hashtable ht = new Hashtable(); 2052 2053 // Set up return hashtable. 2054 2055 ht.put(ServiceStore.FS_SERVICES, services); 2056 2057 // Put in signatures if there. 2058 2059 if (signatures.size() > 0) { 2060 ht.put(ServiceStore.FS_SIGTABLE, signatures); 2061 2062 } 2063 2064 return ht; 2065 } 2066 2067 /** 2068 * Return a Hashtable with key FA_ATTRIBUTES matched to the 2069 * vector of ServiceLocationAttribute objects and key FA_SIG 2070 * matched to the auth block Hashtable for the attributes (if any) 2071 * The attribute objects will have tags matching the tags in 2072 * the input parameter vector. If there are no registrations in any locale, 2073 * FA_ATTRIBUTES is an empty vector. 2074 * 2075 * @param url The ServiceURL for which the records should be returned. 2076 * @param scopes The scope names for which to search. 2077 * @param attrTags The Vector of String 2078 * objects containing the attribute tags. 2079 * @param locale The locale in which to lower case tags and search. 2080 * @return A Hashtable with a vector of ServiceLocationAttribute objects 2081 * as the key and the auth block Hashtable for the attributes 2082 * (if any) as the value. 2083 * If there are no registrations in any locale, FA_ATTRIBUTES 2084 * is an empty vector. 2085 * @exception ServiceLocationException Thrown if any 2086 * error occurs during the operation or if the table 2087 * requires a network connection that failed. This 2088 * includes timeout failures. An error should be 2089 * thrown if the tag vector is for a partial request 2090 * and any of the scopes are protected. 2091 */ 2092 2093 synchronized public Hashtable findAttributes(ServiceURL url, Vector scopes, Vector attrTags, Locale locale)2094 findAttributes(ServiceURL url, 2095 Vector scopes, 2096 Vector attrTags, 2097 Locale locale) 2098 throws ServiceLocationException { 2099 2100 Hashtable ht = new Hashtable(); 2101 Vector ret = new Vector(); 2102 String lang = locale.getLanguage(); 2103 Hashtable sig = null; 2104 2105 // Check whether there are any registrations for this scope/type 2106 // language and, if not, whether there are others. 2107 // in another language, but not this one. 2108 2109 int regStatus = 2110 languageSupported(url.getServiceType().toString(), scopes, lang); 2111 2112 if (regStatus == NO_REGS_IN_LOCALE) { 2113 throw 2114 new ServiceLocationException( 2115 ServiceLocationException.LANGUAGE_NOT_SUPPORTED, 2116 "ssim_lang_unsup", 2117 new Object[] {locale}); 2118 2119 } else if (regStatus == REGS_IN_LOCALE) { 2120 2121 // Only if there are any regs at all. 2122 2123 // Process string tags into pattern objects. Note that, here, 2124 // the patterns are locale specific because the locale of 2125 // the request determines how the attribute tags are lower 2126 // cased. 2127 2128 attrTags = stringVectorToAttributePattern(attrTags, locale); 2129 2130 // Return attributes from the matching URL record. 2131 2132 Hashtable scopeLevel = 2133 (Hashtable)urlScopeLangTable.get(url.toString()); 2134 2135 // If nothing there, then simply return. The URL isn't 2136 // registered. 2137 2138 if (scopeLevel != null) { 2139 2140 // We reuse ht here for attributes. 2141 2142 int i, n = scopes.size(); 2143 2144 for (i = 0; i < n; i++) { 2145 String scope = (String)scopes.elementAt(i); 2146 Hashtable langLevel = 2147 (Hashtable)scopeLevel.get(scope); 2148 2149 // If no registration in this scope, continue. 2150 2151 if (langLevel == null) { 2152 continue; 2153 } 2154 2155 // Get the vector of lists. 2156 2157 Vector listVec = (Vector)langLevel.get(lang); 2158 2159 // If no registration in this locale, continue. 2160 2161 if (listVec == null) { 2162 continue; 2163 2164 } 2165 2166 // Get the service record. 2167 2168 List elem = (List)listVec.elementAt(0); 2169 ServiceRecordInMemory rec = elem.record; 2170 2171 // Once we've found *the* URL record, we can leave the loop 2172 // because there is only one record per locale. 2173 2174 findMatchingAttributes(rec, attrTags, ht, ret); 2175 2176 // Clear out the hashtable. We reuse it for the return. 2177 2178 ht.clear(); 2179 2180 // Store the return vector and the signatures, if any. 2181 2182 ht.put(ServiceStore.FA_ATTRIBUTES, ret); 2183 2184 sig = rec.getAttrSignature(); 2185 2186 if (sig != null) { 2187 ht.put(ServiceStore.FA_SIG, sig); 2188 2189 } 2190 2191 break; 2192 2193 } 2194 } 2195 } 2196 2197 // Put in the empty vector, in case there are no regs at all. 2198 2199 if (ht.size() <= 0) { 2200 ht.put(ServiceStore.FA_ATTRIBUTES, ret); 2201 2202 } 2203 2204 return ht; 2205 } 2206 2207 /** 2208 * Return a Vector of ServiceLocationAttribute objects with attribute tags 2209 * matching the tags in the input parameter vector for all service URL's 2210 * of the service type. If there are no registrations 2211 * in any locale, an empty vector is returned. 2212 * 2213 * @param serviceType The service type name. 2214 * @param scopes The scope names for which to search. 2215 * @param attrTags The Vector of String 2216 * objects containing the attribute tags. 2217 * @param locale The locale in which to lower case tags. 2218 * @return A Vector of ServiceLocationAttribute objects matching the query. 2219 * If no match occurs but there are registrations 2220 * in other locales, null is returned. If there are no registrations 2221 * in any locale, an empty vector is returned. 2222 * @exception ServiceLocationException Thrown if any 2223 * error occurs during the operation or if the table 2224 * requires a network connection that failed. This 2225 * includes timeout failures. An error should also be 2226 * signalled if any of the scopes are protected. 2227 */ 2228 2229 synchronized public Vector findAttributes(String serviceType, Vector scopes, Vector attrTags, Locale locale)2230 findAttributes(String serviceType, 2231 Vector scopes, 2232 Vector attrTags, 2233 Locale locale) 2234 throws ServiceLocationException { 2235 2236 String lang = locale.getLanguage(); 2237 Vector ret = new Vector(); 2238 2239 // Check whether there are any registrations for this type/scope/ 2240 // language and, if not, whether there are others. 2241 // in another language, but not this one. 2242 2243 int regStatus = languageSupported(serviceType, scopes, lang); 2244 2245 if (regStatus == NO_REGS_IN_LOCALE) { 2246 throw 2247 new ServiceLocationException( 2248 ServiceLocationException.LANGUAGE_NOT_SUPPORTED, 2249 "ssim_lang_unsup", 2250 new Object[] {locale}); 2251 2252 } else if (regStatus == REGS_IN_LOCALE) { 2253 2254 // Process string tags into pattern objects. Note that, here, 2255 // the patterns are locale specific because the locale of 2256 // the request determines how the attribute tags are lower 2257 // cased. 2258 2259 attrTags = stringVectorToAttributePattern(attrTags, locale); 2260 int len = attrTags.size(); 2261 2262 // Make a collector for accessing the BtreeVector. 2263 2264 BVCollector collector = 2265 new AttributeBVCollector(attrTags, ret); 2266 int i, n = scopes.size(); 2267 2268 for (i = 0; i < n; i++) { 2269 String scope = (String)scopes.elementAt(i); 2270 String sstKey = 2271 makeScopeTypeLangKey(scope, serviceType, lang); 2272 STLRecord regRecs = (STLRecord)scopeTypeLangTable.get(sstKey); 2273 2274 // If no service type and scope, go to next scope. 2275 2276 if (regRecs == null) { 2277 continue; 2278 } 2279 2280 // Get BtreeVector with all attributes for searching. 2281 2282 BtreeVector bvec = regRecs.attrSort; 2283 2284 // If there are no tags, then simply return everything in 2285 // the BtreeVector. 2286 2287 if (len <= 0) { 2288 bvec.getAll(collector); 2289 2290 } else { 2291 2292 // Use Btree vector to match the attribute tag patterns, 2293 // returning matching records. 2294 2295 int j; 2296 2297 for (j = 0; j < len; j++) { 2298 AttributePattern pat = 2299 (AttributePattern)attrTags.elementAt(j); 2300 2301 bvec.matchEqual(pat, collector); 2302 2303 } 2304 } 2305 } 2306 } 2307 2308 return ret; 2309 } 2310 2311 /** 2312 * Obtain the record matching the service URL and locale. 2313 * 2314 * @param URL The service record to match. 2315 * @param locale The locale of the record. 2316 * @return The ServiceRecord object, or null if none. 2317 */ 2318 2319 synchronized public ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL, Locale locale)2320 getServiceRecord(ServiceURL URL, Locale locale) { 2321 2322 if (URL == null || locale == null) { 2323 return null; 2324 2325 } 2326 2327 // Search in all scopes. 2328 2329 return findExistingRecord(URL, 2330 null, 2331 SLPConfig.localeToLangTag(locale)); 2332 2333 } 2334 2335 /** 2336 * Obtains service records with scopes matching from vector scopes. 2337 * If scopes is null, then returns all records. 2338 * 2339 * @param scopes Vector of scopes to match. 2340 * @return Enumeration Of ServiceRecord Objects. 2341 */ getServiceRecordsByScope(Vector scopes)2342 synchronized public Enumeration getServiceRecordsByScope(Vector scopes) { 2343 2344 // Use a scope collector. 2345 2346 Vector records = new Vector(); 2347 BVCollector collector = 2348 new ScopeBVCollector(records, scopes); 2349 2350 Enumeration keys = scopeTypeLangTable.keys(); 2351 2352 while (keys.hasMoreElements()) { 2353 String sstKey = (String)keys.nextElement(); 2354 STLRecord regRecs = (STLRecord)scopeTypeLangTable.get(sstKey); 2355 2356 // Get all records. 2357 2358 BtreeVector bvec = regRecs.attrSort; 2359 bvec.getAll(collector); 2360 2361 } 2362 2363 return records.elements(); 2364 } 2365 2366 2367 /** 2368 * Dump the service store to the log. 2369 * 2370 */ 2371 dumpServiceStore()2372 synchronized public void dumpServiceStore() { 2373 2374 SLPConfig conf = SLPConfig.getSLPConfig(); 2375 2376 conf.writeLogLine("ssim_dump_start", 2377 new Object[] {this}); 2378 2379 Enumeration keys = scopeTypeLangTable.keys(); 2380 2381 while (keys.hasMoreElements()) { 2382 String sstKey = (String)keys.nextElement(); 2383 STLRecord regRec = (STLRecord)scopeTypeLangTable.get(sstKey); 2384 2385 // If the service type is abstract, then skip it. It will be 2386 // displayed when the concrete type is. 2387 2388 if (regRec.isAbstract) { 2389 continue; 2390 2391 } 2392 2393 // Get all records. 2394 2395 BtreeVector bvec = regRec.attrSort; 2396 Vector vReturns = new Vector(); 2397 BVCollector collector = new AllBVCollector(vReturns); 2398 2399 bvec.getAll(collector); 2400 2401 // Now write them out. 2402 2403 int i, n = vReturns.size(); 2404 2405 for (i = 0; i < n; i++) { 2406 ServiceRecordInMemory rec = 2407 (ServiceRecordInMemory)vReturns.elementAt(i); 2408 2409 writeRecordToLog(conf, rec); 2410 } 2411 } 2412 2413 conf.writeLog("ssim_dump_end", 2414 new Object[] {this}); 2415 } 2416 2417 // 2418 // Protected/private methods. 2419 // 2420 2421 // Register the record without any preliminaries. We assume that 2422 // any old records have been removed and merged into this one, 2423 // as necessary. 2424 registerInternal(ServiceRecordInMemory rec)2425 private void registerInternal(ServiceRecordInMemory rec) { 2426 2427 ServiceURL surl = rec.getServiceURL(); 2428 ServiceType type = surl.getServiceType(); 2429 String serviceType = type.toString(); 2430 String abstractTypeName = type.getAbstractTypeName(); 2431 Locale locale = rec.getLocale(); 2432 String lang = locale.getLanguage(); 2433 Vector scopes = rec.getScopes(); 2434 2435 // Make one age out queue entry. It will go into 2436 // all scopes, but that's OK. 2437 2438 List ageOutElem = addToAgeOutQueue(rec); 2439 2440 // Go through all scopes. 2441 2442 int i, n = scopes.size(); 2443 2444 for (i = 0; i < n; i++) { 2445 String scope = (String)scopes.elementAt(i); 2446 2447 // Initialize the urltable list vector for this URL. 2448 2449 Vector listVec = 2450 initializeURLScopeLangTableVector(surl, scope, lang); 2451 2452 // Add to scope/type/lang table. 2453 2454 addRecordToScopeTypeLangTable(scope, 2455 serviceType, 2456 lang, 2457 false, 2458 rec, 2459 listVec); 2460 2461 // Add a new service type/scope record for this locale. 2462 2463 addTypeLocale(serviceType, scope, lang); 2464 2465 // Add ageOut record, so that it gets deleted when 2466 // the record does. 2467 2468 listVec.addElement(ageOutElem); 2469 2470 // If the type is an abstract type, then add 2471 // separate records. 2472 2473 if (type.isAbstractType()) { 2474 addRecordToScopeTypeLangTable(scope, 2475 abstractTypeName, 2476 lang, 2477 true, 2478 rec, 2479 listVec); 2480 addTypeLocale(abstractTypeName, scope, lang); 2481 2482 } 2483 } 2484 } 2485 2486 // Create a urlScopeLangTable record for this URL. 2487 2488 private Vector initializeURLScopeLangTableVector(ServiceURL url, String scope, String lang)2489 initializeURLScopeLangTableVector(ServiceURL url, 2490 String scope, 2491 String lang) { 2492 2493 // Get scope level, creating if new. 2494 2495 Hashtable scopeLevel = 2496 (Hashtable)urlScopeLangTable.get(url.toString()); 2497 2498 if (scopeLevel == null) { 2499 scopeLevel = new Hashtable(); 2500 urlScopeLangTable.put(url.toString(), scopeLevel); 2501 2502 } 2503 2504 // Get lang level, creating if new. 2505 2506 Hashtable langLevel = 2507 (Hashtable)scopeLevel.get(scope); 2508 2509 if (langLevel == null) { 2510 langLevel = new Hashtable(); 2511 scopeLevel.put(scope, langLevel); 2512 2513 } 2514 2515 // Check whether there's anything already there. 2516 // Bug if so. 2517 2518 Assert.slpassert(langLevel.get(lang) == null, 2519 "ssim_url_lang_botch", 2520 new Object[] {lang, 2521 url, 2522 scope}); 2523 2524 // Add a new list vector, and return it. 2525 2526 Vector listVec = new Vector(); 2527 2528 langLevel.put(lang, listVec); 2529 2530 return listVec; 2531 2532 } 2533 2534 // Add a record to the scope/type/language table. 2535 2536 private void addRecordToScopeTypeLangTable(String scope, String serviceType, String lang, boolean isAbstract, ServiceRecordInMemory rec, Vector listVec)2537 addRecordToScopeTypeLangTable(String scope, 2538 String serviceType, 2539 String lang, 2540 boolean isAbstract, 2541 ServiceRecordInMemory rec, 2542 Vector listVec) { 2543 2544 // Make key for scope/type/language table. 2545 2546 String stlKey = makeScopeTypeLangKey(scope, serviceType, lang); 2547 2548 // Get record for scope/type/lang. 2549 2550 STLRecord trec = (STLRecord)scopeTypeLangTable.get(stlKey); 2551 2552 // If it's not there, make it. 2553 2554 if (trec == null) { 2555 trec = new STLRecord(isAbstract); 2556 scopeTypeLangTable.put(stlKey, trec); 2557 2558 } 2559 2560 // Otherwise, add record to all. 2561 2562 addRecordToAttrValueSort(trec.attrValueSort, rec, listVec); 2563 addRecordToAttrSort(trec.attrSort, rec, listVec); 2564 2565 } 2566 2567 // Add a new record into the attr value table. 2568 2569 private void addRecordToAttrValueSort(Hashtable table, ServiceRecordInMemory rec, Vector listVec)2570 addRecordToAttrValueSort(Hashtable table, 2571 ServiceRecordInMemory rec, 2572 Vector listVec) { 2573 2574 Vector attrList = rec.getAttrList(); 2575 int i, n = attrList.size(); 2576 2577 // Go through the attribute list. 2578 2579 for (i = 0; i < n; i++) { 2580 ServerAttribute attr = 2581 (ServerAttribute)attrList.elementAt(i); 2582 AttributeString tag = attr.idPattern; 2583 Vector values = attr.values; 2584 2585 // If a type table record exists, use it. Otherwise, 2586 // create a newly initialized one. 2587 2588 Hashtable ttable = (Hashtable)table.get(tag); 2589 2590 if (ttable == null) { 2591 ttable = makeAttrTypeTable(); 2592 table.put(tag, ttable); 2593 2594 } 2595 2596 // Get the class of values. 2597 2598 String typeKey = null; 2599 2600 if (values == null) { 2601 2602 // We're done, since there are no attributes to add. 2603 2604 continue; 2605 2606 } else { 2607 Object val = values.elementAt(0); 2608 2609 typeKey = val.getClass().getName(); 2610 } 2611 2612 // Get the BtreeVector. 2613 2614 BtreeVector bvec = 2615 (BtreeVector)ttable.get(typeKey); 2616 2617 // Insert a record for each value. 2618 2619 int j, m = values.size(); 2620 2621 for (j = 0; j < m; j++) { 2622 List elem = bvec.add(values.elementAt(j), rec); 2623 2624 // Put the element into the deletion table. 2625 2626 listVec.addElement(elem); 2627 } 2628 } 2629 } 2630 2631 // Return a newly initialized attribute type table. It will 2632 // have a hash for each allowed type, with a new BtreeVector 2633 // attached. 2634 makeAttrTypeTable()2635 private Hashtable makeAttrTypeTable() { 2636 2637 Hashtable ret = new Hashtable(); 2638 2639 ret.put(INTEGER_TYPE, new BtreeVector()); 2640 ret.put(ATTRIBUTE_STRING_TYPE, new BtreeVector()); 2641 ret.put(BOOLEAN_TYPE, new BtreeVector()); 2642 ret.put(OPAQUE_TYPE, new BtreeVector()); 2643 2644 return ret; 2645 } 2646 2647 // Add a new record into the attrs table. 2648 2649 private void addRecordToAttrSort(BtreeVector table, ServiceRecordInMemory rec, Vector listVec)2650 addRecordToAttrSort(BtreeVector table, 2651 ServiceRecordInMemory rec, 2652 Vector listVec) { 2653 2654 Vector attrList = rec.getAttrList(); 2655 int i, n = attrList.size(); 2656 2657 // If no attributes, then add with empty string as 2658 // the attribute tag. 2659 2660 if (n <= 0) { 2661 List elem = 2662 table.add(new AttributeString("", rec.getLocale()), rec); 2663 2664 listVec.addElement(elem); 2665 2666 return; 2667 } 2668 2669 // Iterate through the attribute list, adding to the 2670 // BtreeVector with attribute as the sort key. 2671 2672 for (i = 0; i < n; i++) { 2673 ServerAttribute attr = 2674 (ServerAttribute)attrList.elementAt(i); 2675 2676 List elem = table.add(attr.idPattern, rec); 2677 2678 // Save for deletion. 2679 2680 listVec.addElement(elem); 2681 2682 } 2683 } 2684 2685 // Add a record to the ageOut queue. 2686 addToAgeOutQueue(ServiceRecordInMemory rec)2687 private List addToAgeOutQueue(ServiceRecordInMemory rec) { 2688 2689 Long exTime = new Long(rec.getExpirationTime()); 2690 return ageOutQueue.add(exTime, rec); 2691 2692 } 2693 2694 // Remove the URL record from the database. 2695 2696 private void deregisterInternal(ServiceURL url, Vector scopes, String lang)2697 deregisterInternal(ServiceURL url, Vector scopes, String lang) { 2698 2699 ServiceType type = url.getServiceType(); 2700 2701 // To deregister, we only need to find the Vector of List objects 2702 // containing the places where this registration is hooked into 2703 // lists and unhook them. Garbage collection of other structures 2704 // is handled during insertion or in deregisterTypeLocale(), 2705 // if there are no more registrations at all. 2706 2707 // Find the scope table.. 2708 2709 Hashtable scopeLangTable = 2710 (Hashtable)urlScopeLangTable.get(url.toString()); 2711 2712 // If it's not there, then maybe not registered. 2713 2714 if (scopeLangTable == null) { 2715 return; 2716 2717 } 2718 2719 // For each scope, find the lang table. 2720 2721 int i, n = scopes.size(); 2722 2723 for (i = 0; i < n; i++) { 2724 String scope = (String)scopes.elementAt(i); 2725 2726 Hashtable langTable = (Hashtable)scopeLangTable.get(scope); 2727 2728 if (langTable == null) { 2729 continue; 2730 } 2731 2732 // If the locale is non-null, then just deregister from this 2733 // locale. 2734 2735 if (lang != null) { 2736 deregisterFromLocale(langTable, lang); 2737 2738 // Record the deletion in the scope/type table, and 2739 // also the number of regs table. 2740 2741 deleteTypeLocale(type.toString(), scope, lang); 2742 2743 // Check for abstract type as well. 2744 2745 if (type.isAbstractType()) { 2746 deleteTypeLocale(type.getAbstractTypeName(), scope, lang); 2747 2748 } 2749 2750 } else { 2751 2752 // Otherwise, deregister all languages. 2753 2754 Enumeration en = langTable.keys(); 2755 2756 while (en.hasMoreElements()) { 2757 lang = (String)en.nextElement(); 2758 2759 deregisterFromLocale(langTable, lang); 2760 2761 // Record the deletion in the scope/type table, and 2762 // also the number of regs table. 2763 2764 deleteTypeLocale(type.toString(), scope, lang); 2765 2766 // Check for abstract type as well. 2767 2768 if (type.isAbstractType()) { 2769 deleteTypeLocale(type.getAbstractTypeName(), 2770 scope, 2771 lang); 2772 2773 } 2774 } 2775 } 2776 2777 // If the table is empty, then remove the lang table. 2778 2779 if (langTable.size() <= 0) { 2780 scopeLangTable.remove(scope); 2781 2782 } 2783 } 2784 2785 // If all languages were deleted, delete the 2786 // urlScopeLangTable record. Other GC handled in 2787 // deleteTypeLocale(). 2788 2789 if (scopeLangTable.size() <= 0) { 2790 urlScopeLangTable.remove(url.toString()); 2791 2792 } 2793 2794 } 2795 2796 // Deregister a single locale from the language table. 2797 deregisterFromLocale(Hashtable langTable, String lang)2798 private void deregisterFromLocale(Hashtable langTable, String lang) { 2799 2800 // Get the Vector containing the list of registrations. 2801 2802 Vector regList = (Vector)langTable.get(lang); 2803 2804 Assert.slpassert(regList != null, 2805 "ssim_null_reg_vector", 2806 new Object[] {lang}); 2807 2808 // Walk down the list of registrations and unhook them from 2809 // their respective lists. 2810 2811 int i, n = regList.size(); 2812 2813 for (i = 0; i < n; i++) { 2814 List elem = (List)regList.elementAt(i); 2815 2816 elem.delete(); 2817 2818 } 2819 2820 // Remove the locale record. 2821 2822 langTable.remove(lang); 2823 } 2824 2825 // Find an existing record matching the URL by searching in all scopes. 2826 // The record will be the same for all scopes in the same language. 2827 // If locale is null, return any. If there are none, return null. 2828 2829 private ServiceRecordInMemory findExistingRecord(ServiceURL surl, Vector scopes, String lang)2830 findExistingRecord(ServiceURL surl, Vector scopes, String lang) { 2831 2832 ServiceRecordInMemory rec = null; 2833 2834 // Look in urlScopeLangTable. 2835 2836 Hashtable scopeLevel = 2837 (Hashtable)urlScopeLangTable.get(surl.toString()); 2838 2839 if (scopeLevel != null) { 2840 2841 // If scopes is null, then perform the search for all 2842 // scopes in the table. Otherwise perform it for 2843 // all scopes incoming. 2844 2845 Enumeration en = null; 2846 2847 if (scopes == null) { 2848 en = scopeLevel.keys(); 2849 2850 } else { 2851 en = scopes.elements(); 2852 2853 } 2854 2855 while (en.hasMoreElements()) { 2856 String scope = (String)en.nextElement(); 2857 Hashtable langLevel = (Hashtable)scopeLevel.get(scope); 2858 2859 // If no langLevel table, continue searching. 2860 2861 if (langLevel == null) { 2862 continue; 2863 2864 } 2865 2866 Vector listVec = null; 2867 2868 // Use lang tag if we have it, otherwise, pick arbitrary. 2869 2870 if (lang != null) { 2871 listVec = (Vector)langLevel.get(lang); 2872 2873 } else { 2874 Enumeration llen = langLevel.elements(); 2875 2876 listVec = (Vector)llen.nextElement(); 2877 2878 } 2879 2880 // If none for this locale, try the next scope. 2881 2882 if (listVec == null) { 2883 continue; 2884 2885 } 2886 2887 // Select out the record. 2888 2889 List elem = (List)listVec.elementAt(0); 2890 2891 rec = elem.record; 2892 break; 2893 2894 } 2895 } 2896 2897 return rec; 2898 } 2899 2900 // Find attributes matching the record and place the matching attributes 2901 // into the vector. Use the hashtable for collation. 2902 2903 static void findMatchingAttributes(ServiceRecordInMemory rec, Vector attrTags, Hashtable ht, Vector ret)2904 findMatchingAttributes(ServiceRecordInMemory rec, 2905 Vector attrTags, 2906 Hashtable ht, 2907 Vector ret) 2908 throws ServiceLocationException { 2909 2910 int len = attrTags.size(); 2911 Vector attrList = rec.getAttrList(); 2912 2913 // For each attribute, go through the tag vector If an attribute 2914 // matches, merge it into the return vector. 2915 2916 int i, n = attrList.size(); 2917 2918 for (i = 0; i < n; i++) { 2919 ServerAttribute attr = 2920 (ServerAttribute)attrList.elementAt(i); 2921 2922 // All attributes match if the pattern vector is 2923 // empty. 2924 2925 if (len <= 0) { 2926 saveValueIfMatch(attr, null, ht, ret); 2927 2928 } else { 2929 2930 // Check each pattern against the attribute id. 2931 2932 int j; 2933 2934 for (j = 0; j < len; j++) { 2935 AttributePattern attrTag = 2936 (AttributePattern)attrTags.elementAt(j); 2937 2938 saveValueIfMatch(attr, attrTag, ht, ret); 2939 2940 } 2941 } 2942 } 2943 } 2944 2945 // Check the attribute against the pattern. If the pattern is null, 2946 // then match occurs. Merge the attribute into the vector 2947 // if match. 2948 saveValueIfMatch(ServerAttribute attr, AttributePattern attrTag, Hashtable ht, Vector ret)2949 static private void saveValueIfMatch(ServerAttribute attr, 2950 AttributePattern attrTag, 2951 Hashtable ht, 2952 Vector ret) 2953 throws ServiceLocationException { 2954 2955 AttributeString id = attr.idPattern; 2956 2957 // We save the attribute value if either 2958 // the pattern is null or it matches the attribute id. 2959 2960 if (attrTag == null || attrTag.match(id)) { 2961 2962 Vector values = attr.getValues(); 2963 2964 // Create new values vector so record copy isn't 2965 // modified. 2966 2967 if (values != null) { 2968 values = (Vector)values.clone(); 2969 2970 } 2971 2972 // Create new attribute so record copy isn't 2973 // modified. 2974 2975 ServiceLocationAttribute nattr = 2976 new ServiceLocationAttribute(attr.getId(), values); 2977 2978 // Merge duplicate attributes into vector. 2979 2980 ServiceLocationAttribute.mergeDuplicateAttributes(nattr, 2981 ht, 2982 ret, 2983 true); 2984 } 2985 } 2986 2987 // Check whether the incoming scopes are the same as existing 2988 // scopes. 2989 2990 private void checkScopeStatus(ServiceURL surl, Vector scopes, short errCode)2991 checkScopeStatus(ServiceURL surl, 2992 Vector scopes, 2993 short errCode) 2994 throws ServiceLocationException { 2995 2996 // Drill down in the urlScopeLangTable table. 2997 2998 Hashtable scopeLevel = 2999 (Hashtable)urlScopeLangTable.get(surl.toString()); 3000 3001 if (scopeLevel == null) { 3002 return; // not yet registered... 3003 3004 } 3005 3006 // We need to have exactly the same scopes as in 3007 // the registration. 3008 3009 int i, n = scopes.size(); 3010 boolean ok = true; 3011 3012 if (n != scopeLevel.size()) { 3013 ok = false; 3014 3015 } else { 3016 3017 for (i = 0; i < n; i++) { 3018 if (scopeLevel.get(scopes.elementAt(i)) == null) { 3019 ok = false; 3020 break; 3021 3022 } 3023 } 3024 } 3025 3026 if (!ok) { 3027 throw 3028 new ServiceLocationException(errCode, 3029 "ssim_scope_mis", 3030 new Object[0]); 3031 3032 } 3033 } 3034 3035 // Check whether an existing nonservice URL is registered under 3036 // a different service type. 3037 checkForExistingUnderOtherServiceType(ServiceURL url, Vector scopes)3038 private void checkForExistingUnderOtherServiceType(ServiceURL url, 3039 Vector scopes) 3040 throws ServiceLocationException { 3041 3042 // Drill down in the urlScopeLangTable table. 3043 3044 Hashtable scopeLevel = 3045 (Hashtable)urlScopeLangTable.get(url.toString()); 3046 3047 if (scopeLevel == null) { 3048 return; // not yet registered. 3049 3050 } 3051 3052 // Get hashtable of locale records under scopes. Any scope 3053 // will do. 3054 3055 Object scope = scopes.elementAt(0); 3056 3057 Hashtable localeLevel = (Hashtable)scopeLevel.get(scope); 3058 3059 Assert.slpassert(localeLevel != null, 3060 "ssim_null_lang_table", 3061 new Object[] {scope}); 3062 3063 // Get a record from any locale. 3064 3065 Enumeration en = localeLevel.elements(); 3066 3067 Assert.slpassert(en.hasMoreElements(), 3068 "ssim_empty_lang_table", 3069 new Object[] {scope}); 3070 3071 // Get vector of registrations. 3072 3073 Vector vec = (Vector)en.nextElement(); 3074 3075 Assert.slpassert(vec.size() > 0, 3076 "ssim_empty_reg_vector", 3077 new Object[] {scope}); 3078 3079 List elem = (List)vec.elementAt(0); 3080 3081 // OK, now check the registration. 3082 3083 ServiceURL recURL = elem.record.getServiceURL(); 3084 ServiceType recType = recURL.getServiceType(); 3085 3086 if (!recType.equals(url.getServiceType())) { 3087 throw 3088 new ServiceLocationException( 3089 ServiceLocationException.INVALID_UPDATE, 3090 "ssim_st_already", 3091 new Object[0]); 3092 } 3093 } 3094 3095 // Merge old record into new record. 3096 mergeOldRecordIntoNew(ServiceRecordInMemory oldRec, ServiceRecordInMemory newRec)3097 final private void mergeOldRecordIntoNew(ServiceRecordInMemory oldRec, 3098 ServiceRecordInMemory newRec) 3099 throws ServiceLocationException { 3100 3101 Vector newAttrs = newRec.getAttrList(); 3102 Vector oldAttrs = oldRec.getAttrList(); 3103 Hashtable ht = new Hashtable(); 3104 3105 // Charge up the hashtable with the new attributes. 3106 3107 int i, n = newAttrs.size(); 3108 3109 for (i = 0; i < n; i++) { 3110 ServerAttribute attr = 3111 (ServerAttribute)newAttrs.elementAt(i); 3112 3113 ht.put(attr.getId().toLowerCase(), attr); 3114 3115 } 3116 3117 // Merge in the old attributes. 3118 3119 n = oldAttrs.size(); 3120 3121 for (i = 0; i < n; i++) { 3122 ServerAttribute attr = 3123 (ServerAttribute)oldAttrs.elementAt(i); 3124 3125 if (ht.get(attr.getId().toLowerCase()) == null) { 3126 newAttrs.addElement(attr); 3127 3128 } 3129 } 3130 3131 // Change the attribute vector on the rec. 3132 3133 newRec.setAttrList(newAttrs); 3134 3135 // Merge old scopes into new. 3136 3137 Vector oldScopes = oldRec.getScopes(); 3138 Vector newScopes = newRec.getScopes(); 3139 int j, m = oldScopes.size(); 3140 3141 for (j = 0; j < m; j++) { 3142 String scope = (String)oldScopes.elementAt(j); 3143 3144 if (!newScopes.contains(scope)) { 3145 newScopes.addElement(scope); 3146 3147 } 3148 } 3149 3150 // Note that we don't have to merge security because there 3151 // will never be an incremental update to a record 3152 // in a protected scope. 3153 3154 // Change the scope vector on the rec. 3155 3156 newRec.setScopes(newScopes); 3157 3158 } 3159 3160 // Delete attributes matching attrTags. 3161 deleteAttributes(ServiceRecordInMemory rec, Vector attrTags)3162 private void deleteAttributes(ServiceRecordInMemory rec, 3163 Vector attrTags) 3164 throws ServiceLocationException { 3165 3166 // For each attribute, go through the tag vector and put attributes 3167 // that do not match the tags into the new attribute vector. 3168 3169 Vector attrList = rec.getAttrList(); 3170 3171 // If there are no attributes for this one, then simply return. 3172 3173 if (attrList.size() <= 0) { 3174 return; 3175 3176 } 3177 3178 int i, n = attrList.size(); 3179 Vector newAttrList = new Vector(); 3180 int len = attrTags.size(); 3181 3182 for (i = 0; i < n; i++) { 3183 ServerAttribute attr = 3184 (ServerAttribute)attrList.elementAt(i); 3185 AttributeString id = attr.idPattern; 3186 boolean deleteIt = false; 3187 3188 int j; 3189 3190 // Now check the tags. 3191 3192 for (j = 0; j < len; j++) { 3193 AttributePattern attrTag = 3194 (AttributePattern)attrTags.elementAt(j); 3195 3196 // If there's a match, mark for deletion. 3197 3198 if (attrTag.match(id)) { 3199 deleteIt = true; 3200 break; 3201 3202 } 3203 } 3204 3205 if (!deleteIt) { 3206 newAttrList.addElement(attr); 3207 } 3208 } 3209 3210 // Replace the attribute vector in the record. 3211 3212 rec.setAttrList(newAttrList); 3213 } 3214 3215 // Convert a vector of attribute tag strings to attribute pattern objects. 3216 stringVectorToAttributePattern(Vector tags, Locale locale)3217 private Vector stringVectorToAttributePattern(Vector tags, Locale locale) 3218 throws ServiceLocationException { 3219 3220 // Takes care of findAttributes() case where no vector. 3221 3222 if (tags == null) { 3223 return null; 3224 3225 } 3226 3227 Vector v = new Vector(); 3228 int i, n = tags.size(); 3229 3230 for (i = 0; i < n; i++) { 3231 String value = (String)tags.elementAt(i); 3232 3233 AttributePattern tag = 3234 new AttributePattern(value, locale); 3235 3236 if (!v.contains(tag)) { 3237 v.addElement(tag); 3238 3239 } 3240 } 3241 3242 return v; 3243 } 3244 3245 // 3246 // Output of service store to log. 3247 // 3248 3249 // Write record to config log file. 3250 3251 private void writeRecordToLog(SLPConfig conf, ServiceStore.ServiceRecord rec)3252 writeRecordToLog(SLPConfig conf, ServiceStore.ServiceRecord rec) { 3253 3254 Locale locale = rec.getLocale(); 3255 ServiceURL surl = rec.getServiceURL(); 3256 Vector scopes = rec.getScopes(); 3257 Vector attributes = rec.getAttrList(); 3258 long exTime = rec.getExpirationTime(); 3259 Hashtable urlSig = rec.getURLSignature(); 3260 Hashtable attrSig = rec.getAttrSignature(); 3261 3262 conf.writeLogLine("ssim_dump_entry_start", new Object[0]); 3263 conf.writeLogLine("ssim_dump_entry", 3264 new Object[] { 3265 locale, 3266 surl.toString(), 3267 Integer.toString(surl.getLifetime()), 3268 Long.toString(((exTime - System.currentTimeMillis())/1000)), 3269 surl.getServiceType(), 3270 scopes, 3271 attributes}); 3272 3273 if (urlSig != null) { 3274 conf.writeLogLine("ssim_dump_urlsig", 3275 new Object[] {urlSig}); 3276 3277 } 3278 3279 if (attrSig != null) { 3280 conf.writeLogLine("ssim_dump_attrsig", 3281 new Object[] { 3282 attrSig}); 3283 3284 } 3285 conf.writeLogLine("ssim_entry_end", new Object[0]); 3286 } 3287 3288 // 3289 // Utilities for dealing with service type/scope locale table. 3290 // 3291 3292 // Bump up the number of registrations for this service type, scope and 3293 // locale. 3294 3295 private void addTypeLocale(String type, String scope, String lang)3296 addTypeLocale(String type, String scope, String lang) { 3297 3298 String sstKey = makeScopeTypeKey(scope, type); 3299 3300 // Get any existing record. 3301 3302 Hashtable langTable = (Hashtable)sstLocales.get(sstKey); 3303 3304 // Insert a new one if none there. 3305 3306 if (langTable == null) { 3307 langTable = new Hashtable(); 3308 3309 sstLocales.put(sstKey, langTable); 3310 3311 } 3312 3313 // Look up locale. 3314 3315 Integer numRegs = (Integer)langTable.get(lang); 3316 3317 // Add a new one if none there, otherwise, bump up old. 3318 3319 if (numRegs == null) { 3320 numRegs = new Integer(1); 3321 3322 } else { 3323 numRegs = new Integer(numRegs.intValue() + 1); 3324 3325 } 3326 3327 // Put it back. 3328 3329 langTable.put(lang, numRegs); 3330 3331 } 3332 3333 // Bump down the number of registrations for this service type, scope, 3334 // in all locales. 3335 deleteTypeLocale(String type, String scope, String lang)3336 private void deleteTypeLocale(String type, String scope, String lang) { 3337 3338 String sstKey = makeScopeTypeKey(scope, type); 3339 3340 // Get any existing record. 3341 3342 Hashtable langTable = (Hashtable)sstLocales.get(sstKey); 3343 3344 // If none there, then error. But this should have been caught 3345 // during deletion, so it's fatal. 3346 3347 Assert.slpassert(langTable != null, 3348 "ssim_ssttable_botch", 3349 new Object[] { 3350 type, 3351 scope}); 3352 3353 // Get the Integer object recording the number of registrations. 3354 3355 Integer numRegs = (Integer)langTable.get(lang); 3356 3357 Assert.slpassert(numRegs != null, 3358 "ssim_ssttable_lang_botch", 3359 new Object[] { 3360 lang, 3361 type, 3362 scope}); 3363 3364 // Bump down by one, remove if zero. 3365 3366 numRegs = new Integer(numRegs.intValue() - 1); 3367 3368 if (numRegs.intValue() <= 0) { 3369 langTable.remove(lang); 3370 3371 if (langTable.size() <= 0) { 3372 sstLocales.remove(sstKey); 3373 3374 } 3375 3376 // Garbage collection. 3377 3378 // Remove records from the scopeTypeLangTable, 3379 // since there are no registrations left for this 3380 // type/scope/locale. 3381 3382 String stlKey = 3383 makeScopeTypeLangKey(scope, type, lang); 3384 scopeTypeLangTable.remove(stlKey); 3385 3386 } else { 3387 3388 // Put it back. 3389 3390 langTable.put(lang, numRegs); 3391 3392 } 3393 } 3394 3395 // Return REGS if the language is supported. Supported means that the 3396 // there are some registrations of this service type in it or that 3397 // there are none in any locale. Return NO_REGS if there are absolutely 3398 // no registrations whatsoever, in any language. Return NO_REGS_IN_LOCALE 3399 // if there are no registrations in that language but there are in 3400 // others. 3401 3402 private int languageSupported(String type, Vector scopes, String lang)3403 languageSupported(String type, Vector scopes, String lang) { 3404 3405 // Look through scope vector. 3406 3407 boolean otherLangRegs = false; 3408 boolean sameLangRegs = false; 3409 int i, n = scopes.size(); 3410 3411 for (i = 0; i < n; i++) { 3412 String scope = (String)scopes.elementAt(i); 3413 String sstKey = makeScopeTypeKey(scope, type); 3414 3415 // Get any existing record. 3416 3417 Hashtable langTable = (Hashtable)sstLocales.get(sstKey); 3418 3419 // If there are no regs, then check next scope. 3420 3421 if (langTable == null) { 3422 continue; 3423 3424 } 3425 3426 Object numRegs = langTable.get(lang); 3427 3428 // Check whether there are other language regs 3429 // or same language regs. 3430 3431 if (numRegs == null) { 3432 otherLangRegs = true; 3433 3434 } else { 3435 sameLangRegs = true; 3436 3437 } 3438 } 3439 3440 // Return appropriate code. 3441 3442 if (otherLangRegs == false && 3443 sameLangRegs == false) { 3444 return NO_REGS; 3445 3446 } else if (otherLangRegs == true && 3447 sameLangRegs == false) { 3448 return NO_REGS_IN_LOCALE; 3449 3450 } else { 3451 return REGS_IN_LOCALE; 3452 3453 } 3454 } 3455 3456 // 3457 // Hash key calculations and hash table structuring. 3458 // 3459 3460 // Return a key for type and scope. 3461 makeScopeTypeKey(String scope, String type)3462 private String makeScopeTypeKey(String scope, String type) { 3463 return scope + "/" + type; 3464 3465 } 3466 3467 // Make a hash key consisting of the scope and service type. 3468 3469 final private String makeScopeTypeLangKey(String scope, String serviceType, String lang)3470 makeScopeTypeLangKey(String scope, 3471 String serviceType, 3472 String lang) { 3473 3474 return scope + "/" + serviceType + "/" + lang; 3475 } 3476 3477 // Return the key's scope. 3478 keyScope(String key)3479 final private String keyScope(String key) { 3480 int idx = key.indexOf('/'); 3481 String ret = ""; 3482 3483 if (idx > 0) { 3484 ret = key.substring(0, idx); 3485 } 3486 3487 return ret; 3488 } 3489 3490 3491 // Return the key's service type/NA. 3492 keyServiceType(String key)3493 final private String keyServiceType(String key) { 3494 int idx = key.indexOf('/'); 3495 String ret = ""; 3496 int len = key.length(); 3497 3498 if (idx >= 0 && idx < len - 1) { 3499 ret = key.substring(idx+1, len); 3500 } 3501 3502 // Parse off the final lang. 3503 3504 idx = ret.indexOf('/'); 3505 3506 ret = ret.substring(0, idx); 3507 3508 return ret; 3509 } 3510 3511 // Return true if the record is for an abstract type. 3512 isAbstractTypeRecord(String sstKey)3513 final private boolean isAbstractTypeRecord(String sstKey) { 3514 STLRecord rec = (STLRecord)scopeTypeLangTable.get(sstKey); 3515 3516 return rec.isAbstract; 3517 3518 } 3519 3520 } 3521