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