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 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 78 ParserBVCollector(Vector scopes) { 79 this.scopes = scopes; 80 81 } 82 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 135 AttributeBVCollector(Vector attrTags, Vector ret) { 136 this.attrTags = attrTags; 137 this.ret = ret; 138 139 } 140 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 175 ScopeBVCollector(Vector records, Vector scopes) { 176 this.records = records; 177 this.scopes = scopes; 178 179 } 180 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 222 AllBVCollector(Vector records) { 223 this.records = records; 224 225 } 226 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 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 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 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 310 RegRecord(Object value) { 311 this.value = value; 312 313 } 314 315 // Add a new record to the buckets, return new element. 316 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 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 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 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 370 Vector getContents() { 371 return contents; 372 373 } 374 375 // Add the entire contents of the vector to the return record. 376 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1205 public Hashtable getAttrSignature() { 1206 return attrSig; 1207 1208 } 1209 1210 1211 // 1212 // Package-local methods. 1213 1214 final void setAttrList(Vector newList) { 1215 attrList = newList; 1216 1217 } 1218 1219 final void setScopes(Vector newScopes) { 1220 scopes = newScopes; 1221 1222 } 1223 1224 final void setURLSignature(Hashtable nauth) { 1225 urlSig = nauth; 1226 1227 } 1228 1229 final void setAttrSignature(Hashtable nauth) { 1230 attrSig = nauth; 1231 1232 } 1233 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 3513 final private boolean isAbstractTypeRecord(String sstKey) { 3514 STLRecord rec = (STLRecord)scopeTypeLangTable.get(sstKey); 3515 3516 return rec.isAbstract; 3517 3518 } 3519 3520 } 3521