2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A */
2N/A
2N/A// ServiceStoreInMemory.java: An in-memory implementation
2N/A// of the service store.
2N/A// Author: James Kempf
2N/A// Created On: Mon Oct 20 12:36:35 1997
2N/A// Last Modified By: James Kempf
2N/A// Last Modified On: Tue Mar 2 15:32:23 1999
2N/A// Update Count: 472
2N/A//
2N/A
2N/Apackage com.sun.slp;
2N/A
2N/Aimport java.util.*;
2N/Aimport java.io.*;
2N/A
2N/A/**
2N/A * The ServiceStoreInMemory class implements the ServiceStore interface
2N/A * on in-memory data structures.
2N/A * <details of those structures here>
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/Aclass ServiceStoreInMemory extends Object implements ServiceStore {
2N/A
2N/A /**
2N/A * The BVCollector interface allows various
2N/A * data structures to collect stuff from the BtreeVector.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private interface BVCollector {
2N/A
2N/A // Set the return value.
2N/A
2N/A abstract void setReturn(ServiceRecordInMemory rec);
2N/A
2N/A }
2N/A
2N/A /**
2N/A * The ParserBVCollector class implements a BtreeVector
2N/A * collector for the parser.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class ParserBVCollector extends Object implements BVCollector {
2N/A
2N/A Parser.ParserRecord prReturns = null;
2N/A private Vector scopes = null;
2N/A
2N/A ParserBVCollector(Vector scopes) {
2N/A this.scopes = scopes;
2N/A
2N/A }
2N/A
2N/A public void setReturn(ServiceRecordInMemory rec) {
2N/A
2N/A Hashtable services = prReturns.services;
2N/A Hashtable signatures = prReturns.signatures;
2N/A ServiceURL surl = rec.getServiceURL();
2N/A
2N/A // Add if we don't already have it.
2N/A
2N/A if (services.get(surl) == null) {
2N/A Vector s = (Vector)rec.getScopes().clone();
2N/A
2N/A DATable.filterScopes(s, scopes, false);
2N/A
2N/A // Need to adjust lifetime to reflect the time to live. Don't
2N/A // set the lifetime if it has already expired.
2N/A
2N/A long lifetime =
2N/A (rec.getExpirationTime() -
2N/A System.currentTimeMillis()) / 1000;
2N/A
2N/A if (lifetime > 0) {
2N/A ServiceURL url =
2N/A new ServiceURL(surl.toString(), (int)lifetime);
2N/A
2N/A services.put(surl, s);
2N/A
2N/A Hashtable sig = rec.getURLSignature();
2N/A
2N/A if (sig != null) {
2N/A signatures.put(url, sig);
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The AttributeBVCollector class implements a BtreeVector
2N/A * collector for the collecting attribute values by type.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class AttributeBVCollector extends Object implements BVCollector {
2N/A
2N/A private Hashtable alreadySeen = new Hashtable();
2N/A // records already seen.
2N/A private Vector attrTags = null; // tags to match against records
2N/A private Hashtable ht = new Hashtable(); // for collecting attributes.
2N/A private Vector ret = null; // for returns.
2N/A
2N/A AttributeBVCollector(Vector attrTags, Vector ret) {
2N/A this.attrTags = attrTags;
2N/A this.ret = ret;
2N/A
2N/A }
2N/A
2N/A public void setReturn(ServiceRecordInMemory rec) {
2N/A
2N/A // If we've got it already, then don't add again.
2N/A
2N/A if (alreadySeen.get(rec) == null) {
2N/A alreadySeen.put(rec, rec);
2N/A
2N/A try {
2N/A findMatchingAttributes(rec, attrTags, ht, ret);
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A Assert.slpassert(false,
2N/A "ssim_attrbvc_botch",
2N/A new Object[] {ex.getMessage()});
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The ScopeBVCollector class implements a BtreeVector
2N/A * collector for the collecting records if scopes match.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class ScopeBVCollector extends Object implements BVCollector {
2N/A
2N/A private Hashtable alreadySeen = new Hashtable();
2N/A // for those we've seen
2N/A private Vector records = null; // for returns.
2N/A private Vector scopes = null; // the scopes we're looking for
2N/A
2N/A ScopeBVCollector(Vector records, Vector scopes) {
2N/A this.records = records;
2N/A this.scopes = scopes;
2N/A
2N/A }
2N/A
2N/A public void setReturn(ServiceRecordInMemory rec) {
2N/A
2N/A // If we've got it already, then don't add.
2N/A
2N/A if (alreadySeen.get(rec) == null) {
2N/A alreadySeen.put(rec, rec);
2N/A
2N/A if (scopes == null) {
2N/A records.addElement(rec);
2N/A
2N/A } else {
2N/A
2N/A // Check scopes.
2N/A
2N/A int i;
2N/A Vector rscopes = rec.getScopes();
2N/A int len = scopes.size();
2N/A
2N/A for (i = 0; i < len; i++) {
2N/A if (rscopes.contains(scopes.elementAt(i))) {
2N/A records.addElement(rec);
2N/A break;
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The AllBVCollector class implements a BtreeVector
2N/A * collector for collecting all records.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class AllBVCollector extends Object implements BVCollector {
2N/A
2N/A private Vector records = null; // for returns.
2N/A
2N/A AllBVCollector(Vector records) {
2N/A this.records = records;
2N/A
2N/A }
2N/A
2N/A public void setReturn(ServiceRecordInMemory rec) {
2N/A
2N/A // If we've got it already, then don't add.
2N/A
2N/A if (!records.contains(rec)) {
2N/A records.addElement(rec);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The List class implements a linked list for storing records
2N/A * in the BtreeVector structure.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class List extends Object {
2N/A
2N/A ServiceRecordInMemory record = null;
2N/A List next = null;
2N/A List prev = null;
2N/A
2N/A // Create a new list object.
2N/A
2N/A List(ServiceRecordInMemory record) {
2N/A this.record = record;
2N/A
2N/A }
2N/A
2N/A // Insert a new record after this one. Return the new
2N/A // record.
2N/A
2N/A synchronized List insertAfter(ServiceRecordInMemory record) {
2N/A List newRec = new List(record);
2N/A newRec.next = next;
2N/A newRec.prev = this;
2N/A
2N/A if (next != null) {
2N/A next.prev = newRec;
2N/A
2N/A }
2N/A
2N/A this.next = newRec;
2N/A
2N/A return newRec;
2N/A
2N/A }
2N/A
2N/A // Delete this record from the list.
2N/A
2N/A synchronized void delete() {
2N/A
2N/A if (next != null) {
2N/A next.prev = prev;
2N/A }
2N/A
2N/A if (prev != null) {
2N/A prev.next = next;
2N/A
2N/A }
2N/A
2N/A prev = null;
2N/A next = null;
2N/A
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The RegRecord class implements a record with the value for the
2N/A * record buckets. It is used as elements in BtreeVector.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class RegRecord extends Object {
2N/A
2N/A Object value = null; // the value for these registrations.
2N/A List head = new List(null); // head of the list always null,
2N/A // never changes.
2N/A // Construct a new one.
2N/A
2N/A RegRecord(Object value) {
2N/A this.value = value;
2N/A
2N/A }
2N/A
2N/A // Add a new record to the buckets, return new element.
2N/A
2N/A List add(ServiceRecordInMemory rec) {
2N/A
2N/A return head.insertAfter(rec);
2N/A
2N/A }
2N/A
2N/A // For every element in record's list, set the return value in the
2N/A // returns object. Since deletions may have removed everything
2N/A // from this record, return true only if something was there.
2N/A
2N/A boolean setReturn(BVCollector returns) {
2N/A
2N/A boolean match = false;
2N/A List l = head;
2N/A
2N/A for (l = l.next; l != null; l = l.next) {
2N/A ServiceRecordInMemory rec = l.record;
2N/A returns.setReturn(rec);
2N/A match = true;
2N/A
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A public String toString() {
2N/A return "<RegRecord value="+value+"list="+head.next+">";
2N/A
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The BtreeVector class stores registrations in sorted order. The
2N/A * Quicksort algorithm is used to insert items and search for something.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class BtreeVector extends Object {
2N/A
2N/A // Contains the sorted vector.
2N/A
2N/A private Vector contents = new Vector();
2N/A
2N/A public String toString() {
2N/A return "<BtreeVector "+contents.toString()+">";
2N/A
2N/A }
2N/A
2N/A // Return the contents as a sorted vector of RegRecord.
2N/A // Note that this doesn't return a copy, so
2N/A // the vector can be side-effected.
2N/A
2N/A Vector getContents() {
2N/A return contents;
2N/A
2N/A }
2N/A
2N/A // Add the entire contents of the vector to the return record.
2N/A
2N/A boolean getAll(BVCollector returns) {
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A // Add a new record to this vector. We also garbage collect any
2N/A // records that are empty. Return the list object added.
2N/A
2N/A List add(Object value, ServiceRecordInMemory record) {
2N/A RegRecord rec = walkVector(value, true); // an update...
2N/A
2N/A // Add the record to this one.
2N/A
2N/A return rec.add(record);
2N/A
2N/A }
2N/A
2N/A
2N/A // Add only if no element in the vector matches the tag.
2N/A
2N/A boolean matchDoesNotContain(Object pattern, BVCollector returns) {
2N/A
2N/A // Go through the vector, putting in anything that isn't equal.
2N/A
2N/A int i, n = contents.size();
2N/A Vector noMatch = new Vector();
2N/A boolean match = false;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (!compareEqual(rec.value, pattern)) {
2N/A
2N/A // Add to prospective returns.
2N/A
2N/A noMatch.addElement(rec);
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // If we got this far, there are some no matches.
2N/A
2N/A n = noMatch.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)noMatch.elementAt(i);
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A
2N/A }
2N/A
2N/A return match;
2N/A
2N/A }
2N/A
2N/A boolean
2N/A matchEqual(Object pattern, BVCollector returns) {
2N/A
2N/A boolean match = false;
2N/A
2N/A // We can't walk the vector if the value is an AttributePattern,
2N/A // because equals doesn't apply.
2N/A
2N/A if (pattern instanceof AttributePattern) {
2N/A int i, n = contents.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A AttributeString val = (AttributeString)rec.value;
2N/A AttributePattern pat = (AttributePattern)pattern;
2N/A
2N/A if (pat.match(val)) {
2N/A match = match | rec.setReturn(returns);
2N/A
2N/A }
2N/A }
2N/A } else {
2N/A RegRecord rec = walkVector(pattern, false);
2N/A // not an update...
2N/A
2N/A // If nothing came back, return false.
2N/A
2N/A if (rec == null) {
2N/A match = false;
2N/A
2N/A } else {
2N/A
2N/A // Otherwise set returns in the vector.
2N/A
2N/A match = rec.setReturn(returns);
2N/A
2N/A }
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A boolean
2N/A matchNotEqual(Object pattern, BVCollector returns) {
2N/A
2N/A // Go through the vector, putting in anything that isn't equal.
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (!compareEqual(rec.value, pattern)) {
2N/A match = match | rec.setReturn(returns);
2N/A
2N/A }
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A boolean
2N/A matchLessEqual(Object pattern,
2N/A BVCollector returns) {
2N/A
2N/A // Go through the vector, putting in anything that is
2N/A // less than or equal.
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (!compareLessEqual(rec.value, pattern)) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A boolean
2N/A matchNotLessEqual(Object pattern,
2N/A BVCollector returns) {
2N/A // Go through the vector, putting in anything that is not
2N/A // less than or equal. Start at the top.
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = n - 1; i >= 0; i--) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (compareLessEqual(rec.value, pattern)) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A boolean
2N/A matchGreaterEqual(Object pattern,
2N/A BVCollector returns) {
2N/A // Go through the vector, putting in anything that is greater
2N/A // than or equal. Start at the top.
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = n - 1; i >= 0; i--) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (!compareGreaterEqual(rec.value, pattern)) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A boolean
2N/A matchNotGreaterEqual(Object pattern,
2N/A BVCollector returns) {
2N/A // Go through the vector, putting in anything that is not
2N/A // than or equal.
2N/A
2N/A int i, n = contents.size();
2N/A boolean match = false;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A RegRecord rec = (RegRecord)contents.elementAt(i);
2N/A
2N/A if (compareGreaterEqual(rec.value, pattern)) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A match = match | rec.setReturn(returns);
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A // Binary tree walk the vector, performing the operation. Note that
2N/A // we use dynamic typing heavily here to get maximum code reuse.
2N/A
2N/A private RegRecord
2N/A walkVector(Object pattern, boolean update) {
2N/A
2N/A // Get the starting set of indicies.
2N/A
2N/A int size = contents.size();
2N/A int middle = size / 2;
2N/A int top = size - 1;
2N/A int bottom = 0;
2N/A RegRecord rec = null;
2N/A
2N/A top = (top < 0 ? 0:top);
2N/A
2N/A while (size > 0) {
2N/A
2N/A // Get the one at the current middle.
2N/A
2N/A rec = (RegRecord)contents.elementAt(middle);
2N/A
2N/A // Garbage Collection.
2N/A // If it was null, then delete. But only if we're
2N/A // inserting. We leave it alone on lookup.
2N/A
2N/A if (update) {
2N/A if (rec.head.next == null) {
2N/A
2N/A contents.removeElementAt(middle);
2N/A
2N/A size = size - 1;
2N/A middle = bottom + (size / 2);
2N/A top = top - 1;
2N/A
2N/A top = (top < 0 ? 0:top);
2N/A
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A // Compare value to record, if equal, return record.
2N/A // code.
2N/A
2N/A if (compareEqual(rec.value, pattern)) {
2N/A return rec;
2N/A
2N/A } else if (compareLessEqual(pattern, rec.value)) {
2N/A
2N/A // Recalculate index. We move left, because the value is
2N/A // less that the value in the vector, so an equal value
2N/A // must be to the left. Note that the top is not in the
2N/A // interval because it has already been checked and
2N/A // found wanting.
2N/A
2N/A top = middle;
2N/A size = (top - bottom);
2N/A middle = top - (size / 2);
2N/A middle = (middle < 0 ? 0:middle);
2N/A
2N/A if (middle == top) {
2N/A
2N/A // Neither top nor middle are in the interval,
2N/A // so size is zero. We need to compare with bottom.
2N/A
2N/A rec = null;
2N/A RegRecord trec = (RegRecord)contents.elementAt(bottom);
2N/A
2N/A if (update) {
2N/A rec = new RegRecord(pattern);
2N/A
2N/A // If the pattern is equal to bottom, return it.
2N/A // If the pattern is less than or equal to bottom,
2N/A // we insert it at bottom. If it is greater
2N/A // than or equal, we insert it at middle.
2N/A
2N/A if (compareEqual(trec.value, pattern)) {
2N/A return trec;
2N/A
2N/A } else if (compareLessEqual(pattern, trec.value)) {
2N/A
2N/A // Pattern is less than bottom, so insert
2N/A // at bottom.
2N/A
2N/A contents.insertElementAt(rec, bottom);
2N/A
2N/A } else {
2N/A contents.insertElementAt(rec, middle);
2N/A
2N/A }
2N/A } else {
2N/A
2N/A // If it equals bottom, then return bottom rec.
2N/A
2N/A if (compareEqual(trec.value, pattern)) {
2N/A rec = trec;
2N/A
2N/A }
2N/A }
2N/A
2N/A break;
2N/A
2N/A }
2N/A
2N/A } else if (compareGreaterEqual(pattern, rec.value)) {
2N/A
2N/A // Recalculate index. We move right, because the value is
2N/A // greater that the value in the vector, so an equal
2N/A // value must be to the right. Note that the top is not
2N/A // in the interval because it has already been checked
2N/A // and found wanting.
2N/A
2N/A bottom = middle;
2N/A size = (top - bottom);
2N/A middle = bottom + (size / 2);
2N/A
2N/A if (middle == bottom) {
2N/A
2N/A // Neither bottom nor middle is in the interval,
2N/A // so size is zero. We need to compare with top.
2N/A
2N/A rec = null;
2N/A RegRecord trec = (RegRecord)contents.elementAt(top);
2N/A
2N/A if (update) {
2N/A rec = new RegRecord(pattern);
2N/A
2N/A // If the pattern is equal to the top, we
2N/A // return the top. If the pattern is greater
2N/A // then top, we insert it after top, else we
2N/A // insert it at top.
2N/A
2N/A if (compareEqual(trec.value, pattern)) {
2N/A return trec;
2N/A
2N/A } else if (compareGreaterEqual(pattern,
2N/A trec.value)) {
2N/A
2N/A // Pattern is greater than top, so insert
2N/A // after top.
2N/A
2N/A int i = top + 1;
2N/A
2N/A if (i >= contents.size()) {
2N/A contents.addElement(rec);
2N/A
2N/A } else {
2N/A contents.insertElementAt(rec, i);
2N/A
2N/A }
2N/A } else {
2N/A
2N/A // Pattern is less than top, so insert at
2N/A // top, causing top to move up.
2N/A
2N/A contents.insertElementAt(rec, top);
2N/A
2N/A }
2N/A } else {
2N/A
2N/A // If it equals top, then return top rec.
2N/A
2N/A if (compareEqual(trec.value, pattern)) {
2N/A rec = trec;
2N/A
2N/A }
2N/A }
2N/A
2N/A break;
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Take care of update where vector is empty or cleaned out.
2N/A
2N/A if (update && rec == null) {
2N/A rec = new RegRecord(pattern);
2N/A
2N/A Assert.slpassert((contents.size() == 0),
2N/A "ssim_btree_botch",
2N/A new Object[0]);
2N/A
2N/A contents.addElement(rec);
2N/A }
2N/A
2N/A return rec;
2N/A }
2N/A
2N/A // Add any registrations that match the pattern.
2N/A
2N/A boolean
2N/A compareEqual(Object target, Object pattern) {
2N/A
2N/A if (target instanceof Integer ||
2N/A target instanceof Boolean ||
2N/A target instanceof Opaque ||
2N/A target instanceof Long) {
2N/A if (pattern.equals(target)) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof AttributeString) {
2N/A
2N/A // If the pattern is an AttributePattern instead of an
2N/A // AttributeString, the subclass method will get invoked.
2N/A
2N/A if (((AttributeString)pattern).match(
2N/A (AttributeString)target)) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else {
2N/A Assert.slpassert(false,
2N/A "ssim_unk_qtype",
2N/A new Object[] {pattern.getClass().getName()});
2N/A }
2N/A
2N/A return false;
2N/A
2N/A }
2N/A
2N/A // Add any registrations that are less than or equal to the pattern.
2N/A
2N/A boolean
2N/A compareLessEqual(Object target, Object pattern) {
2N/A
2N/A if (target instanceof Integer) {
2N/A if (((Integer)target).intValue() <=
2N/A ((Integer)pattern).intValue()) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof AttributeString) {
2N/A
2N/A if (((AttributeString)target).lessEqual(
2N/A (AttributeString)pattern)) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof Long) {
2N/A if (((Long)target).longValue() <=
2N/A ((Long)pattern).longValue()) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof Boolean ||
2N/A target instanceof Opaque) {
2N/A if (target.toString().compareTo(pattern.toString()) <= 0) {
2N/A return true;
2N/A
2N/A }
2N/A } else {
2N/A Assert.slpassert(false,
2N/A "ssim_unk_qtype",
2N/A new Object[] {target.getClass().getName()});
2N/A }
2N/A
2N/A return false;
2N/A
2N/A }
2N/A
2N/A // Add any registrations that are greater than or equal to the pattern.
2N/A
2N/A boolean
2N/A compareGreaterEqual(Object target, Object pattern) {
2N/A
2N/A if (target instanceof Integer) {
2N/A if (((Integer)target).intValue() >=
2N/A ((Integer)pattern).intValue()) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof AttributeString) {
2N/A
2N/A if (((AttributeString)target).greaterEqual(
2N/A (AttributeString)pattern)) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof Long) {
2N/A if (((Long)target).longValue() >=
2N/A ((Long)pattern).longValue()) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else if (target instanceof Boolean ||
2N/A target instanceof Opaque) {
2N/A if (target.toString().compareTo(pattern.toString()) >= 0) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A } else {
2N/A Assert.slpassert(false,
2N/A "ssim_unk_qtype",
2N/A new Object[] {target.getClass().getName()});
2N/A }
2N/A
2N/A return false;
2N/A
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The InMemoryEvaluator evaluates queries for ServiceStoreInMemory.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class InMemoryEvaluator implements Parser.QueryEvaluator {
2N/A
2N/A private Hashtable attrLevel; // Sorted attribute table.
2N/A private BtreeVector attrLevelNot; // Used for universal negation.
2N/A private Vector inScopes; // Input scopes.
2N/A private ParserBVCollector returns; // For gathering results.
2N/A
2N/A InMemoryEvaluator(Hashtable ht,
2N/A BtreeVector btv,
2N/A Vector nscopes) {
2N/A attrLevel = ht;
2N/A attrLevelNot = btv;
2N/A inScopes = nscopes;
2N/A returns = new ParserBVCollector(inScopes);
2N/A
2N/A
2N/A }
2N/A
2N/A // Evaluate the query by matching the attribute tag and
2N/A // value, using the operator. If invert is true, then
2N/A // return records that do NOT match.
2N/A
2N/A public boolean
2N/A evaluate(AttributeString tag,
2N/A char op,
2N/A Object pattern,
2N/A boolean invert,
2N/A Parser.ParserRecord prReturns)
2N/A throws ServiceLocationException {
2N/A
2N/A boolean match = false;
2N/A returns.prReturns = prReturns;
2N/A
2N/A // If inversion is on, then gather all from the
2N/A // table of registrations that do NOT have this
2N/A // attribute.
2N/A
2N/A if (invert) {
2N/A match = attrLevelNot.matchDoesNotContain(tag, returns);
2N/A
2N/A }
2N/A
2N/A // Find the table of classes v.s. sorted value vectors.
2N/A
2N/A Hashtable ttable = (Hashtable)attrLevel.get(tag);
2N/A
2N/A // If attribute not present, then simply return.
2N/A
2N/A if (ttable == null) {
2N/A
2N/A return match;
2N/A
2N/A }
2N/A
2N/A // If operator is present, then return all.
2N/A
2N/A if (op == Parser.PRESENT) {
2N/A
2N/A // ...but only if invert isn't on.
2N/A
2N/A if (!invert) {
2N/A
2N/A // We use attrLevelNot to get all, because it
2N/A // will also pick up keywords. There are
2N/A // no keywords in attrLevel because keywords
2N/A // don't have any values.
2N/A
2N/A match = attrLevelNot.matchEqual(tag, returns);
2N/A
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A
2N/A // We know that the type table is fully initialized with
2N/A // BtreeVectors for each type.
2N/A
2N/A // Get the pattern's class. Pattern will not be null because
2N/A // the parser has checked for it and PRESENT has been
2N/A // filtered out above.
2N/A
2N/A Class pclass = pattern.getClass();
2N/A String typeKey = pclass.getName();
2N/A
2N/A // If the class is AttributePattern, then use AttributeString
2N/A // instead.
2N/A
2N/A if (pattern instanceof AttributePattern) {
2N/A typeKey = pclass.getSuperclass().getName();
2N/A
2N/A }
2N/A
2N/A // If invert is on, collect those whose types don't match as
2N/A // well.
2N/A
2N/A if (invert) {
2N/A Enumeration en = ttable.keys();
2N/A
2N/A while (en.hasMoreElements()) {
2N/A String key = (String)en.nextElement();
2N/A
2N/A // Only record if the type does NOT match.
2N/A
2N/A if (!key.equals(typeKey)) {
2N/A BtreeVector bvec = (BtreeVector)ttable.get(key);
2N/A
2N/A match = match | bvec.getAll(returns);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Get the sorted value vector corresponding to the value class.
2N/A
2N/A BtreeVector bvec = (BtreeVector)ttable.get(typeKey);
2N/A
2N/A // Do the appropriate thing for the operator.
2N/A
2N/A switch (op) {
2N/A
2N/A case Parser.EQUAL:
2N/A
2N/A if (!invert) {
2N/A match = bvec.matchEqual(pattern, returns);
2N/A
2N/A } else {
2N/A match = bvec.matchNotEqual(pattern, returns);
2N/A
2N/A }
2N/A break;
2N/A
2N/A case Parser.LESS:
2N/A
2N/A // Note that we've filtered out Opaque, Boolean, and wildcarded
2N/A // strings before calling this method.
2N/A
2N/A if (!invert) {
2N/A match = bvec.matchLessEqual(pattern, returns);
2N/A
2N/A } else {
2N/A match = bvec.matchNotLessEqual(pattern, returns);
2N/A
2N/A }
2N/A break;
2N/A
2N/A case Parser.GREATER:
2N/A
2N/A // Note that we've filtered out Opaque and Boolean
2N/A // before calling this method.
2N/A
2N/A if (!invert) {
2N/A match = bvec.matchGreaterEqual(pattern, returns);
2N/A
2N/A } else {
2N/A match = bvec.matchNotGreaterEqual(pattern, returns);
2N/A
2N/A }
2N/A break;
2N/A
2N/A default:
2N/A Assert.slpassert(false,
2N/A "ssim_unk_qop",
2N/A new Object[] {new Character((char)op)});
2N/A }
2N/A
2N/A return match;
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * The ServiceRecordInMemory class implements the
2N/A * ServiceStore.ServiceRecord interface on in-memory data structures.
2N/A * Each property is implemented as an instance variable.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class ServiceRecordInMemory extends Object
2N/A implements ServiceStore.ServiceRecord {
2N/A
2N/A private ServiceURL serviceURL = null; // the service URL
2N/A private Vector attrList = null; // the attribute list
2N/A private Locale locale = null; // the locale
2N/A private long timeToDie = 0; // when the record should die.
2N/A private Vector scopes = null; // the scopes
2N/A private Hashtable urlSig = null;
2N/A // URL signature block list, if any.
2N/A private Hashtable attrSig = null;
2N/A // Attribute signature block list, if any.
2N/A
2N/A // Create a ServiceStoreInMemory record.
2N/A
2N/A ServiceRecordInMemory(ServiceURL surl, Vector alist,
2N/A Vector nscopes, Locale loc,
2N/A Hashtable nurlSig,
2N/A Hashtable nattrSig) {
2N/A
2N/A // All need to be nonnull.
2N/A
2N/A Assert.nonNullParameter(surl, "surl");
2N/A Assert.nonNullParameter(alist, "alist");
2N/A Assert.nonNullParameter(nscopes, "nscopes");
2N/A Assert.nonNullParameter(loc, "loc");
2N/A
2N/A serviceURL = surl;
2N/A attrList = attributeVectorToServerAttribute(alist, loc);
2N/A scopes = nscopes;
2N/A locale = loc;
2N/A urlSig = nurlSig;
2N/A attrSig = nattrSig;
2N/A
2N/A int lifetime = serviceURL.getLifetime();
2N/A
2N/A timeToDie = lifetime * 1000 + System.currentTimeMillis();
2N/A }
2N/A
2N/A /**
2N/A * Return the ServiceURL for the record.
2N/A *
2N/A * @return The record's service URL.
2N/A */
2N/A
2N/A public final ServiceURL getServiceURL() {
2N/A return serviceURL;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the Vector of ServerAttribute objects for the record.
2N/A *
2N/A * @return Vector of ServerAttribute objects for the record.
2N/A */
2N/A
2N/A public final Vector getAttrList() {
2N/A return attrList;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the locale of the registration.
2N/A *
2N/A * @return The locale of the registration.
2N/A */
2N/A
2N/A public final Locale getLocale() {
2N/A return locale;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the Vector of scopes in which the record is registered.
2N/A *
2N/A * @return Vector of strings with scope names.
2N/A */
2N/A
2N/A public final Vector getScopes() {
2N/A return scopes;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the expiration time for the record. This informs the
2N/A * service store when the record should expire and be removed
2N/A * from the table.
2N/A *
2N/A * @return The expiration time for the record.
2N/A */
2N/A
2N/A public long getExpirationTime() {
2N/A return timeToDie;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the URL signature list.
2N/A *
2N/A * @return URL signature block list.
2N/A */
2N/A
2N/A public Hashtable getURLSignature() {
2N/A return urlSig;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return the attribute signature list.
2N/A *
2N/A * @return Attribute signature list.
2N/A */
2N/A
2N/A public Hashtable getAttrSignature() {
2N/A return attrSig;
2N/A
2N/A }
2N/A
2N/A
2N/A //
2N/A // Package-local methods.
2N/A
2N/A final void setAttrList(Vector newList) {
2N/A attrList = newList;
2N/A
2N/A }
2N/A
2N/A final void setScopes(Vector newScopes) {
2N/A scopes = newScopes;
2N/A
2N/A }
2N/A
2N/A final void setURLSignature(Hashtable nauth) {
2N/A urlSig = nauth;
2N/A
2N/A }
2N/A
2N/A final void setAttrSignature(Hashtable nauth) {
2N/A attrSig = nauth;
2N/A
2N/A }
2N/A
2N/A public String toString() {
2N/A
2N/A String ret = "{";
2N/A
2N/A ret +=
2N/A serviceURL + ", " + locale + ", " + attrList + ", " +
2N/A scopes + ", " + locale + ", " + urlSig + ", " + attrSig;
2N/A
2N/A ret += "}";
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A // Convert a vector of ServiceLocationAttribute objects to
2N/A // ServerAttibutes.
2N/A
2N/A private Vector
2N/A attributeVectorToServerAttribute(Vector attrs, Locale locale) {
2N/A int i, n = attrs.size();
2N/A Vector v = new Vector();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServiceLocationAttribute attr =
2N/A (ServiceLocationAttribute)attrs.elementAt(i);
2N/A
2N/A v.addElement(new ServerAttribute(attr, locale));
2N/A }
2N/A
2N/A return v;
2N/A }
2N/A
2N/A }
2N/A
2N/A /**
2N/A * A record for scopeTypeLangTable table,
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A private class STLRecord extends Object {
2N/A
2N/A Hashtable attrValueSort = new Hashtable();
2N/A // Table of attributes, sorted by value.
2N/A BtreeVector attrSort = new BtreeVector(); // Btree of attributes.
2N/A boolean isAbstract = false;
2N/A // True if the record is for an abstract
2N/A // type.
2N/A STLRecord(boolean isAbstract) {
2N/A this.isAbstract = isAbstract;
2N/A
2N/A }
2N/A }
2N/A
2N/A //
2N/A // ServiceStoreInMemory instance variables.
2N/A //
2N/A
2N/A // ServiceStoreInMemory maintains an invaraint that the record for a
2N/A // particular URL, set of scopes, and locale is the same object
2N/A // (pointer-wise) regardless of where it is inserted into the table.
2N/A // So it can be compared with ==.
2N/A
2N/A // The scopeTypeLangTable
2N/A //
2N/A // Keys for this table are scope/service type/lang tag. Values are
2N/A // STLRecord objects. The STLRecord.attrValueSort field is a Hashtable
2N/A // where all registrations *having* the attribute tag keys in the
2N/A // table are contained. This table is used in queries for positive
2N/A // logical expressions. The STLRecord.attrSort field is a BtreeVector
2N/A // keyed by attribute. It is used for negative queries to find all
2N/A // records not having a particular attribute and to find all
2N/A // registrations. The STLRecord.isAbstract field tells whether the record
2N/A // is for an abstract type name.
2N/A //
2N/A // The values in the STLRecord.attrValueSort hashtable are themselves
2N/A // hashtables. These hashtables are keyed by one of the type keys below,
2N/A // with the values being BtreeVector objects. The BtreeVector objects
2N/A // contain sorted lists of RegRecord objects for Integer,
2N/A // AttributeString, Boolean, and Opaque types. All records having
2N/A // values equal to the value in the RegRecord are put into a list
2N/A // on the RegRecord. There is no STLRecord.attrValueSort
2N/A // hashtable for keyword attributes because they have no values.
2N/A // The parser evaluator must use the STLRecord.attrSort hashtable when a
2N/A // present operator is encountered (the only valid operator with a
2N/A // keyword).
2N/A //
2N/A // The values in the STLRecord.attrSort BtreeVector are RegRecord
2N/A // objects with all records having that attribute tag being on the
2N/A // RegRecord list.
2N/A
2N/A // Keys for the various types.
2N/A
2N/A private final static String INTEGER_TYPE = "java.lang.Integer";
2N/A private final static String ATTRIBUTE_STRING_TYPE =
2N/A "com.sun.slp.AttributeString";
2N/A private final static String BOOLEAN_TYPE = "java.lang.Boolean";
2N/A private final static String OPAQUE_TYPE = "com.sun.slp.Opaque";
2N/A
2N/A private Hashtable scopeTypeLangTable = new Hashtable();
2N/A
2N/A // The urlScopeLangTable
2N/A //
2N/A // Keys for this table are service url as a string. We don't use
2N/A // the service URL itself because the hash code depends on the
2N/A // current service type rather than the original, and we need
2N/A // to be able to distinguish for a non-service: URL if a
2N/A // registration comes in with a different service type from the
2N/A // original. Values are hashtables with key being scope name,
2N/A // values are hashtables with lang tag key. Ultimate values are
2N/A // a vector of List objects for lists in which List.record is
2N/A // inserted. This table is used to perform deletions and for
2N/A // finding the attributes associated with a particular URL.
2N/A
2N/A private Hashtable urlScopeLangTable = new Hashtable();
2N/A
2N/A // The sstLocales Table
2N/A //
2N/A // The scope/service type v.s. number of languages. Keys are
2N/A // the type/scope, values are a hashtable keyed by lang tag.
2N/A // Values in the lang tag table are Integer objects giving
2N/A // the number of registrations for that type/scope in the
2N/A // given locale.
2N/A
2N/A private Hashtable sstLocales = new Hashtable();
2N/A
2N/A // A queue of records sorted according to expiration time.
2N/A
2N/A BtreeVector ageOutQueue = new BtreeVector();
2N/A
2N/A // Constants that indicate whether there are any registrations.
2N/A
2N/A private final static int NO_REGS = 0;
2N/A private final static int NO_REGS_IN_LOCALE = 1;
2N/A private final static int REGS_IN_LOCALE = 2;
2N/A
2N/A // Boot time. For DAAdvert timestamps.
2N/A
2N/A private long bootTime = SLPConfig.currentSLPTime();
2N/A
2N/A //
2N/A // ServiceStore Interface Methods.
2N/A //
2N/A
2N/A /**
2N/A * Return the time since the last stateless reboot
2N/A * of the ServiceStore.
2N/A *
2N/A * @return A Long giving the time since the last stateless reboot,
2N/A * in NTP format.
2N/A */
2N/A
2N/A public long getStateTimestamp() {
2N/A
2N/A return bootTime;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Age out all records whose time has expired.
2N/A *
2N/A * @param deleted A Vector for return of ServiceStore.Service records
2N/A * containing deleted services.
2N/A * @return The time interval until another table walk must be done,
2N/A * in milliseconds.
2N/A *
2N/A */
2N/A
2N/A synchronized public long ageOut(Vector deleted) {
2N/A
2N/A // Get the ageOut queue and remove all records whose
2N/A // time has popped.
2N/A
2N/A SLPConfig conf = SLPConfig.getSLPConfig();
2N/A boolean traceDrop = conf.traceDrop();
2N/A Vector queue = ageOutQueue.getContents();
2N/A
2N/A // Go through the queue, dropping records that
2N/A // have expired.
2N/A
2N/A int i;
2N/A
2N/A for (i = 0; i < queue.size(); i++) {
2N/A RegRecord qRec = (RegRecord)queue.elementAt(i);
2N/A long exTime = ((Long)(qRec.value)).longValue();
2N/A long time = System.currentTimeMillis();
2N/A
2N/A // Break out when none expire now.
2N/A
2N/A if (exTime > time) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A // Remove the element from the queue.
2N/A
2N/A /*
2N/A * Must decrement the index 'i' otherwise the next iteration
2N/A * around the loop will miss the element immediately after
2N/A * the element removed.
2N/A *
2N/A * WARNING: Do not use 'i' again until the loop has
2N/A * iterated as it may, after decrementing,
2N/A * be negative.
2N/A */
2N/A queue.removeElementAt(i);
2N/A i--;
2N/A
2N/A // Deregister all on this list. We
2N/A // take specific care to save the next
2N/A // list element before we deregister, otherwise
2N/A // it will be gone after the deregister.
2N/A
2N/A List l = qRec.head.next;
2N/A
2N/A while (l != null) {
2N/A ServiceRecordInMemory rec = l.record;
2N/A ServiceURL url = rec.getServiceURL();
2N/A Vector scopes = rec.getScopes();
2N/A Locale locale = rec.getLocale();
2N/A
2N/A if (traceDrop) {
2N/A conf.writeLog("ssim_ageout",
2N/A new Object[] {
2N/A url,
2N/A rec.getAttrList(),
2N/A scopes,
2N/A locale,
2N/A rec.getURLSignature(),
2N/A rec.getAttrSignature(),
2N/A Long.toString(time),
2N/A Long.toString(exTime)});
2N/A }
2N/A
2N/A // Save the record for the service table, in case more
2N/A // processing needed.
2N/A
2N/A deleted.addElement(rec);
2N/A
2N/A // Save l.next NOW before deregisterInternal() removes it!
2N/A
2N/A l = l.next;
2N/A
2N/A String lang = locale.getLanguage();
2N/A
2N/A deregisterInternal(url, scopes, lang);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Calculate the new sleep time. If there's anything in the vector,
2N/A // then use element 0, because the vector is sorted by time
2N/A // and that will be minimum. Otherwise, use the maximum.
2N/A
2N/A long newSleepy = Defaults.lMaxSleepTime;
2N/A
2N/A if (queue.size() > 0) {
2N/A RegRecord rec = (RegRecord)queue.elementAt(0);
2N/A
2N/A newSleepy =
2N/A ((Long)(rec.value)).longValue() - System.currentTimeMillis();
2N/A
2N/A newSleepy = (newSleepy > 0 ? newSleepy:0);
2N/A // it will wake right up, but
2N/A // so what?
2N/A
2N/A }
2N/A
2N/A return newSleepy;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Create a new registration with the given parameters.
2N/A *
2N/A * @param url The ServiceURL.
2N/A * @param attrs The Vector of ServiceLocationAttribute objects.
2N/A * @param locale The Locale.
2N/A * @param scopes Vector of scopes in which this record is registered.
2N/A * @param urlSig auth block Hashtable for URL signature, or null if none.
2N/A * @param attrSig auth block Hashtable for URL signature, or null if none.
2N/A * @return True if there is an already existing registration that
2N/A * this one replaced.
2N/A * @exception ServiceLocationException Thrown if any
2N/A * error occurs during registration or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public boolean
2N/A register(ServiceURL url, Vector attrs,
2N/A Vector scopes, Locale locale,
2N/A Hashtable urlSig, Hashtable attrSig)
2N/A throws ServiceLocationException {
2N/A
2N/A boolean existing = false;
2N/A
2N/A String lang = locale.getLanguage();
2N/A
2N/A // Find an existing record, in any set of scopes having this language.
2N/A
2N/A ServiceRecordInMemory rec = findExistingRecord(url, null, lang);
2N/A
2N/A // Deregister from existing scopes, if there is an existing record.
2N/A
2N/A if (rec != null) {
2N/A if (urlSig != null) {
2N/A // Ensure that the rereg SPI set and the record's SPI set are
2N/A // equivalent. We need only check the URL sigs here, since
2N/A // this operation is equivalent to a dereg followed by a reg,
2N/A // and dereg requires only URL auth blocks.
2N/A
2N/A Enumeration spis = urlSig.keys();
2N/A while (spis.hasMoreElements()) {
2N/A Object spi = spis.nextElement();
2N/A if (rec.urlSig.remove(spi) == null) {
2N/A throw new ServiceLocationException(
2N/A ServiceLocationException.AUTHENTICATION_FAILED,
2N/A "not_all_spis_present",
2N/A new Object[] {spi});
2N/A }
2N/A }
2N/A if (rec.urlSig.size() != 0) {
2N/A // not all required SPIs were present in SrvReg
2N/A throw new ServiceLocationException(
2N/A ServiceLocationException.AUTHENTICATION_FAILED,
2N/A "not_all_spis_present",
2N/A new Object[] {rec.urlSig.keys()});
2N/A }
2N/A }
2N/A
2N/A deregisterInternal(url, rec.getScopes(), lang);
2N/A existing = true;
2N/A
2N/A }
2N/A
2N/A // Create a new record to register.
2N/A
2N/A rec = new ServiceRecordInMemory(url, attrs, scopes,
2N/A locale, urlSig, attrSig);
2N/A
2N/A // Add new registration.
2N/A
2N/A registerInternal(rec);
2N/A
2N/A return existing;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Deregister a ServiceURL from the database for every locale
2N/A * and every scope. There will be only one record for each URL
2N/A * and locale deregistered, regardless of the number of scopes in
2N/A * which the URL was registered, since the attributes will be the
2N/A * same in each scope if the locale is the same.
2N/A *
2N/A * @param url The ServiceURL
2N/A * @param scopes Vector of scopes.
2N/A * @param urlSig The URL signature, if any.
2N/A * @exception ServiceLocationException Thrown if the
2N/A * ServiceStore does not contain the URL, or if any
2N/A * error occurs during the operation, or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public void
2N/A deregister(ServiceURL url, Vector scopes, Hashtable urlSig)
2N/A throws ServiceLocationException {
2N/A
2N/A // Find existing record. Any locale will do.
2N/A
2N/A ServiceRecordInMemory oldRec =
2N/A findExistingRecord(url, scopes, null);
2N/A
2N/A // Error if none.
2N/A
2N/A if (oldRec == null) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.INVALID_REGISTRATION,
2N/A "ssim_no_rec",
2N/A new Object[] {url});
2N/A
2N/A }
2N/A
2N/A // verify that the dereg SPI set and the record's SPI set are
2N/A // equivalent
2N/A if (urlSig != null) {
2N/A Enumeration spis = urlSig.keys();
2N/A while (spis.hasMoreElements()) {
2N/A Object spi = spis.nextElement();
2N/A if (oldRec.urlSig.remove(spi) == null) {
2N/A throw new ServiceLocationException(
2N/A ServiceLocationException.AUTHENTICATION_FAILED,
2N/A "not_all_spis_present",
2N/A new Object[] {spi});
2N/A }
2N/A }
2N/A if (oldRec.urlSig.size() != 0) {
2N/A // not all required SPIs were present in SrvDereg
2N/A throw new ServiceLocationException(
2N/A ServiceLocationException.AUTHENTICATION_FAILED,
2N/A "not_all_spis_present",
2N/A new Object[] {oldRec.urlSig.keys()});
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Deregister the URL for all locales. Use the recorded service URL
2N/A * because the one passed by the client is possibly incomplete e.g.
2N/A * lacking the service type.
2N/A */
2N/A
2N/A deregisterInternal(oldRec.getServiceURL(), scopes, null);
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Update the service registration with the new parameters, adding
2N/A * attributes and updating the service URL's lifetime.
2N/A *
2N/A * @param url The ServiceURL.
2N/A * @param attrs The Vector of ServiceLocationAttribute objects.
2N/A * @param locale The Locale.
2N/A * @param scopes Vector of scopes in which this record is registered.
2N/A * @exception ServiceLocationException Thrown if any
2N/A * error occurs during registration or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public void
2N/A updateRegistration(ServiceURL url, Vector attrs,
2N/A Vector scopes, Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A String lang = locale.getLanguage();
2N/A ServiceRecordInMemory oldRec =
2N/A findExistingRecord(url, scopes, lang);
2N/A
2N/A // Error if none.
2N/A
2N/A if (oldRec == null) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.INVALID_UPDATE,
2N/A "ssim_no_rec",
2N/A new Object[] {url});
2N/A
2N/A }
2N/A
2N/A // If this is a nonServiceURL, check whether it's registered
2N/A // under a different service type.
2N/A
2N/A ServiceType type = url.getServiceType();
2N/A
2N/A if (!type.isServiceURL()) {
2N/A checkForExistingUnderOtherServiceType(url, scopes);
2N/A
2N/A }
2N/A
2N/A // Deregister the URL in this locale.
2N/A
2N/A deregisterInternal(url, scopes, lang);
2N/A
2N/A // Create a new record to update.
2N/A
2N/A ServiceRecordInMemory rec =
2N/A new ServiceRecordInMemory(url, attrs, scopes,
2N/A locale, null, null);
2N/A
2N/A // Merge old record into new.
2N/A
2N/A mergeOldRecordIntoNew(oldRec, rec);
2N/A
2N/A // Add the new record.
2N/A
2N/A registerInternal(rec);
2N/A }
2N/A
2N/A /**
2N/A * Delete the attributes from the ServiceURL object's table entries.
2N/A * Delete for every locale that has the attributes and every scope.
2N/A * Note that the attribute tags must be lower-cased in the locale of
2N/A * the registration, not in the locale of the request.
2N/A *
2N/A * @param url The ServiceURL.
2N/A * @param scopes Vector of scopes.
2N/A * @param attrTags The Vector of String
2N/A * objects specifying the attribute tags of
2N/A * the attributes to delete.
2N/A * @param locale Locale of the request.
2N/A * @exception ServiceLocationException Thrown if the
2N/A * ServiceStore does not contain the URL or if any
2N/A * error occurs during the operation or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public void
2N/A deleteAttributes(ServiceURL url,
2N/A Vector scopes,
2N/A Vector attrTags,
2N/A Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A String lang = SLPConfig.localeToLangTag(locale);
2N/A
2N/A // Get the scope level from urlScopeLangTable.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(url.toString());
2N/A
2N/A // Error if no old record to update.
2N/A
2N/A if (scopeLevel == null) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.INVALID_REGISTRATION,
2N/A "ssim_no_rec",
2N/A new Object[] {url});
2N/A
2N/A }
2N/A
2N/A // Check existing records to be sure that the scopes
2N/A // match. Attributes must be the same across
2N/A // scopes.
2N/A
2N/A checkScopeStatus(url,
2N/A scopes,
2N/A ServiceLocationException.INVALID_REGISTRATION);
2N/A
2N/A // Create attribute patterns for the default locale. This
2N/A // is an optimization. Only Turkish differs in lower
2N/A // case from the default. If there are any other exceptions,
2N/A // we need to move this into the loop.
2N/A
2N/A Vector attrPatterns =
2N/A stringVectorToAttributePattern(attrTags, Defaults.locale);
2N/A
2N/A // Look through the language table for this language at scope level.
2N/A
2N/A Enumeration en = scopeLevel.keys();
2N/A
2N/A Assert.slpassert(en.hasMoreElements(),
2N/A "ssim_empty_scope_table",
2N/A new Object[] {url});
2N/A
2N/A Hashtable ht = new Hashtable();
2N/A boolean foundIt = false;
2N/A
2N/A while (en.hasMoreElements()) {
2N/A String scope = (String)en.nextElement();
2N/A Hashtable langLevel = (Hashtable)scopeLevel.get(scope);
2N/A Enumeration een = langLevel.keys();
2N/A
2N/A Assert.slpassert(een.hasMoreElements(),
2N/A "ssim_empty_lang_table",
2N/A new Object[] {url});
2N/A
2N/A // Find the list of records for this language.
2N/A
2N/A Vector listVec = (Vector)langLevel.get(lang);
2N/A
2N/A if (listVec == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A foundIt = true;
2N/A
2N/A List elem = (List)listVec.elementAt(0);
2N/A ServiceRecordInMemory rec = elem.record;
2N/A Locale loc = rec.getLocale();
2N/A
2N/A // If we've done this one already, go on.
2N/A
2N/A if (ht.get(rec) != null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A ht.put(rec, rec);
2N/A
2N/A // Delete old registration.
2N/A
2N/A deregisterInternal(url, rec.getScopes(), lang);
2N/A
2N/A // Delete attributes from this record.
2N/A
2N/A // If the locale is Turkish, then use the Turkish patterns.
2N/A
2N/A if (loc.getLanguage().equals("tr")) {
2N/A Vector turkishTags =
2N/A stringVectorToAttributePattern(attrTags, loc);
2N/A
2N/A deleteAttributes(rec, turkishTags);
2N/A
2N/A } else {
2N/A deleteAttributes(rec, attrPatterns);
2N/A
2N/A }
2N/A
2N/A // Reregister the record.
2N/A
2N/A registerInternal(rec);
2N/A }
2N/A
2N/A // If no record found, report error.
2N/A
2N/A if (!foundIt) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.INVALID_REGISTRATION,
2N/A "ssim_no_rec_locale",
2N/A new Object[] {url, locale});
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return a Vector of String containing the service types for this
2N/A * scope and naming authority. If there are none, an empty vector is
2N/A * returned.
2N/A *
2N/A * @param namingAuthority The namingAuthority, or "*" if for all.
2N/A * @param scopes The scope names.
2N/A * @return A Vector of String objects that are the type names, or
2N/A * an empty vector if there are none.
2N/A * @exception ServiceLocationException Thrown if any
2N/A * error occurs during the operation or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public Vector
2N/A findServiceTypes(String namingAuthority, Vector scopes)
2N/A throws ServiceLocationException {
2N/A
2N/A Vector ret = new Vector();
2N/A Enumeration keys = scopeTypeLangTable.keys();
2N/A boolean isWildCard = namingAuthority.equals("*");
2N/A boolean isIANA = (namingAuthority.length() <= 0);
2N/A
2N/A // Get all the keys in the table, look for scope.
2N/A
2N/A while (keys.hasMoreElements()) {
2N/A String sstKey = (String)keys.nextElement();
2N/A
2N/A // Check whether this is an abstract type entry.
2N/A // If so, then we ignore it, because we only
2N/A // want full type names in the return.
2N/A
2N/A if (isAbstractTypeRecord(sstKey)) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // If the scope matches then check the naming authority.
2N/A
2N/A String keyScope = keyScope(sstKey);
2N/A
2N/A if (scopes.contains(keyScope)) {
2N/A String keyType = keyServiceType(sstKey);
2N/A
2N/A // If not already there, see if we should add this one to the
2N/A // vector.
2N/A
2N/A if (!ret.contains(keyType)) {
2N/A ServiceType type = new ServiceType(keyType);
2N/A
2N/A // If wildcard, then simply add it to the vector.
2N/A
2N/A if (isWildCard) {
2N/A ret.addElement(type.toString());
2N/A
2N/A } else {
2N/A
2N/A // Check naming authority.
2N/A
2N/A String na = type.getNamingAuthority();
2N/A
2N/A if (type.isNADefault() && isIANA) { // check for IANA..
2N/A ret.addElement(type.toString());
2N/A
2N/A } else if (namingAuthority.equals(na)) { // Not IANA..
2N/A ret.addElement(type.toString());
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A /**
2N/A * Return a Hashtable with the key FS_SERVICES matched to the
2N/A * hashtable of ServiceURL objects as key and a vector
2N/A * of their scopes as value, and the key FS_SIGTABLE
2N/A * matched to a hashtable with ServiceURL objects as key
2N/A * and the auth block Hashtable for the URL (if any) for value. The
2N/A * returned service URLs will match the service type, scope, query,
2N/A * and locale. If there are no signatures, the FS_SIGTABLE
2N/A * key returns null. If there are no
2N/A * registrations in any locale, FS_SERVICES is bound to an
2N/A * empty table.
2N/A *
2N/A * @param serviceType The service type name.
2N/A * @param scope The scope name.
2N/A * @param query The query, with any escaped characters as yet unprocessed.
2N/A * @param locale The locale in which to lowercase query and search.
2N/A * @return A Hashtable with the key FS_SERVICES matched to the
2N/A * hashtable of ServiceURL objects as key and a vector
2N/A * of their scopes as value, and the key FS_SIGTABLE
2N/A * matched to a hashtable with ServiceURL objects as key
2N/A * and the auth block Hashtable for the URL (if any) for value.
2N/A * If there are no registrations in any locale, FS_SERVICES
2N/A * is bound to an empty table.
2N/A * @exception ServiceLocationException Thrown if a parse error occurs
2N/A * during query parsing or if any
2N/A * error occurs during the operation or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures.
2N/A */
2N/A
2N/A synchronized public Hashtable
2N/A findServices(String serviceType,
2N/A Vector scopes,
2N/A String query,
2N/A Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A String lang = locale.getLanguage();
2N/A Parser.ParserRecord ret = new Parser.ParserRecord();
2N/A Hashtable services = null;
2N/A Hashtable signatures = null;
2N/A int i, n = scopes.size();
2N/A int len = 0;
2N/A
2N/A // Get the services and signatures tables.
2N/A
2N/A services = ret.services;
2N/A signatures = ret.signatures;
2N/A
2N/A // Remove leading and trailing spaces.
2N/A
2N/A query = query.trim();
2N/A len = query.length();
2N/A
2N/A // Check whether there are any registrations for this type/scope/
2N/A // language tag and, if not, whether there are others.
2N/A // in another language, but not this one.
2N/A
2N/A int regStatus = languageSupported(serviceType, scopes, lang);
2N/A
2N/A if (regStatus == NO_REGS_IN_LOCALE) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.LANGUAGE_NOT_SUPPORTED,
2N/A "ssim_lang_unsup",
2N/A new Object[] {locale});
2N/A
2N/A } else if (regStatus == REGS_IN_LOCALE) {
2N/A
2N/A // Only do query if regs exist.
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A String sstKey =
2N/A makeScopeTypeLangKey(scope, serviceType, lang);
2N/A STLRecord regRecs =
2N/A (STLRecord)scopeTypeLangTable.get(sstKey);
2N/A
2N/A // If no record for this combo of service type and
2N/A // scope, continue.
2N/A
2N/A if (regRecs == null) {
2N/A continue;
2N/A }
2N/A
2N/A // Special case if the query string is empty. This
2N/A // indicates that all registrations should be returned.
2N/A
2N/A if (len <= 0) {
2N/A BtreeVector bvec = regRecs.attrSort;
2N/A ParserBVCollector collector =
2N/A new ParserBVCollector(scopes);
2N/A collector.prReturns = ret;
2N/A
2N/A // Use the BtreeVector.getAll() method to get all
2N/A // registrations. We will end up revisiting some
2N/A // list elements because there will be ones
2N/A // for multiple attributes, but that will be
2N/A // filtered in the BVCollector.setReturn() method.
2N/A
2N/A bvec.getAll(collector);
2N/A
2N/A } else {
2N/A
2N/A // Otherwise, use the LDAPv3 parser to evaluate.
2N/A
2N/A InMemoryEvaluator ev =
2N/A new InMemoryEvaluator(regRecs.attrValueSort,
2N/A regRecs.attrSort,
2N/A scopes);
2N/A
2N/A Parser.parseAndEvaluateQuery(query, ev, locale, ret);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Create return hashtable.
2N/A
2N/A Hashtable ht = new Hashtable();
2N/A
2N/A // Set up return hashtable.
2N/A
2N/A ht.put(ServiceStore.FS_SERVICES, services);
2N/A
2N/A // Put in signatures if there.
2N/A
2N/A if (signatures.size() > 0) {
2N/A ht.put(ServiceStore.FS_SIGTABLE, signatures);
2N/A
2N/A }
2N/A
2N/A return ht;
2N/A }
2N/A
2N/A /**
2N/A * Return a Hashtable with key FA_ATTRIBUTES matched to the
2N/A * vector of ServiceLocationAttribute objects and key FA_SIG
2N/A * matched to the auth block Hashtable for the attributes (if any)
2N/A * The attribute objects will have tags matching the tags in
2N/A * the input parameter vector. If there are no registrations in any locale,
2N/A * FA_ATTRIBUTES is an empty vector.
2N/A *
2N/A * @param url The ServiceURL for which the records should be returned.
2N/A * @param scopes The scope names for which to search.
2N/A * @param attrTags The Vector of String
2N/A * objects containing the attribute tags.
2N/A * @param locale The locale in which to lower case tags and search.
2N/A * @return A Hashtable with a vector of ServiceLocationAttribute objects
2N/A * as the key and the auth block Hashtable for the attributes
2N/A * (if any) as the value.
2N/A * If there are no registrations in any locale, FA_ATTRIBUTES
2N/A * is an empty vector.
2N/A * @exception ServiceLocationException Thrown if any
2N/A * error occurs during the operation or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures. An error should be
2N/A * thrown if the tag vector is for a partial request
2N/A * and any of the scopes are protected.
2N/A */
2N/A
2N/A synchronized public Hashtable
2N/A findAttributes(ServiceURL url,
2N/A Vector scopes,
2N/A Vector attrTags,
2N/A Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A Hashtable ht = new Hashtable();
2N/A Vector ret = new Vector();
2N/A String lang = locale.getLanguage();
2N/A Hashtable sig = null;
2N/A
2N/A // Check whether there are any registrations for this scope/type
2N/A // language and, if not, whether there are others.
2N/A // in another language, but not this one.
2N/A
2N/A int regStatus =
2N/A languageSupported(url.getServiceType().toString(), scopes, lang);
2N/A
2N/A if (regStatus == NO_REGS_IN_LOCALE) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.LANGUAGE_NOT_SUPPORTED,
2N/A "ssim_lang_unsup",
2N/A new Object[] {locale});
2N/A
2N/A } else if (regStatus == REGS_IN_LOCALE) {
2N/A
2N/A // Only if there are any regs at all.
2N/A
2N/A // Process string tags into pattern objects. Note that, here,
2N/A // the patterns are locale specific because the locale of
2N/A // the request determines how the attribute tags are lower
2N/A // cased.
2N/A
2N/A attrTags = stringVectorToAttributePattern(attrTags, locale);
2N/A
2N/A // Return attributes from the matching URL record.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(url.toString());
2N/A
2N/A // If nothing there, then simply return. The URL isn't
2N/A // registered.
2N/A
2N/A if (scopeLevel != null) {
2N/A
2N/A // We reuse ht here for attributes.
2N/A
2N/A int i, n = scopes.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A Hashtable langLevel =
2N/A (Hashtable)scopeLevel.get(scope);
2N/A
2N/A // If no registration in this scope, continue.
2N/A
2N/A if (langLevel == null) {
2N/A continue;
2N/A }
2N/A
2N/A // Get the vector of lists.
2N/A
2N/A Vector listVec = (Vector)langLevel.get(lang);
2N/A
2N/A // If no registration in this locale, continue.
2N/A
2N/A if (listVec == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // Get the service record.
2N/A
2N/A List elem = (List)listVec.elementAt(0);
2N/A ServiceRecordInMemory rec = elem.record;
2N/A
2N/A // Once we've found *the* URL record, we can leave the loop
2N/A // because there is only one record per locale.
2N/A
2N/A findMatchingAttributes(rec, attrTags, ht, ret);
2N/A
2N/A // Clear out the hashtable. We reuse it for the return.
2N/A
2N/A ht.clear();
2N/A
2N/A // Store the return vector and the signatures, if any.
2N/A
2N/A ht.put(ServiceStore.FA_ATTRIBUTES, ret);
2N/A
2N/A sig = rec.getAttrSignature();
2N/A
2N/A if (sig != null) {
2N/A ht.put(ServiceStore.FA_SIG, sig);
2N/A
2N/A }
2N/A
2N/A break;
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Put in the empty vector, in case there are no regs at all.
2N/A
2N/A if (ht.size() <= 0) {
2N/A ht.put(ServiceStore.FA_ATTRIBUTES, ret);
2N/A
2N/A }
2N/A
2N/A return ht;
2N/A }
2N/A
2N/A /**
2N/A * Return a Vector of ServiceLocationAttribute objects with attribute tags
2N/A * matching the tags in the input parameter vector for all service URL's
2N/A * of the service type. If there are no registrations
2N/A * in any locale, an empty vector is returned.
2N/A *
2N/A * @param serviceType The service type name.
2N/A * @param scopes The scope names for which to search.
2N/A * @param attrTags The Vector of String
2N/A * objects containing the attribute tags.
2N/A * @param locale The locale in which to lower case tags.
2N/A * @return A Vector of ServiceLocationAttribute objects matching the query.
2N/A * If no match occurs but there are registrations
2N/A * in other locales, null is returned. If there are no registrations
2N/A * in any locale, an empty vector is returned.
2N/A * @exception ServiceLocationException Thrown if any
2N/A * error occurs during the operation or if the table
2N/A * requires a network connection that failed. This
2N/A * includes timeout failures. An error should also be
2N/A * signalled if any of the scopes are protected.
2N/A */
2N/A
2N/A synchronized public Vector
2N/A findAttributes(String serviceType,
2N/A Vector scopes,
2N/A Vector attrTags,
2N/A Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A String lang = locale.getLanguage();
2N/A Vector ret = new Vector();
2N/A
2N/A // Check whether there are any registrations for this type/scope/
2N/A // language and, if not, whether there are others.
2N/A // in another language, but not this one.
2N/A
2N/A int regStatus = languageSupported(serviceType, scopes, lang);
2N/A
2N/A if (regStatus == NO_REGS_IN_LOCALE) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.LANGUAGE_NOT_SUPPORTED,
2N/A "ssim_lang_unsup",
2N/A new Object[] {locale});
2N/A
2N/A } else if (regStatus == REGS_IN_LOCALE) {
2N/A
2N/A // Process string tags into pattern objects. Note that, here,
2N/A // the patterns are locale specific because the locale of
2N/A // the request determines how the attribute tags are lower
2N/A // cased.
2N/A
2N/A attrTags = stringVectorToAttributePattern(attrTags, locale);
2N/A int len = attrTags.size();
2N/A
2N/A // Make a collector for accessing the BtreeVector.
2N/A
2N/A BVCollector collector =
2N/A new AttributeBVCollector(attrTags, ret);
2N/A int i, n = scopes.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A String sstKey =
2N/A makeScopeTypeLangKey(scope, serviceType, lang);
2N/A STLRecord regRecs = (STLRecord)scopeTypeLangTable.get(sstKey);
2N/A
2N/A // If no service type and scope, go to next scope.
2N/A
2N/A if (regRecs == null) {
2N/A continue;
2N/A }
2N/A
2N/A // Get BtreeVector with all attributes for searching.
2N/A
2N/A BtreeVector bvec = regRecs.attrSort;
2N/A
2N/A // If there are no tags, then simply return everything in
2N/A // the BtreeVector.
2N/A
2N/A if (len <= 0) {
2N/A bvec.getAll(collector);
2N/A
2N/A } else {
2N/A
2N/A // Use Btree vector to match the attribute tag patterns,
2N/A // returning matching records.
2N/A
2N/A int j;
2N/A
2N/A for (j = 0; j < len; j++) {
2N/A AttributePattern pat =
2N/A (AttributePattern)attrTags.elementAt(j);
2N/A
2N/A bvec.matchEqual(pat, collector);
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A /**
2N/A * Obtain the record matching the service URL and locale.
2N/A *
2N/A * @param URL The service record to match.
2N/A * @param locale The locale of the record.
2N/A * @return The ServiceRecord object, or null if none.
2N/A */
2N/A
2N/A synchronized public ServiceStore.ServiceRecord
2N/A getServiceRecord(ServiceURL URL, Locale locale) {
2N/A
2N/A if (URL == null || locale == null) {
2N/A return null;
2N/A
2N/A }
2N/A
2N/A // Search in all scopes.
2N/A
2N/A return findExistingRecord(URL,
2N/A null,
2N/A SLPConfig.localeToLangTag(locale));
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Obtains service records with scopes matching from vector scopes.
2N/A * If scopes is null, then returns all records.
2N/A *
2N/A * @param scopes Vector of scopes to match.
2N/A * @return Enumeration Of ServiceRecord Objects.
2N/A */
2N/A synchronized public Enumeration getServiceRecordsByScope(Vector scopes) {
2N/A
2N/A // Use a scope collector.
2N/A
2N/A Vector records = new Vector();
2N/A BVCollector collector =
2N/A new ScopeBVCollector(records, scopes);
2N/A
2N/A Enumeration keys = scopeTypeLangTable.keys();
2N/A
2N/A while (keys.hasMoreElements()) {
2N/A String sstKey = (String)keys.nextElement();
2N/A STLRecord regRecs = (STLRecord)scopeTypeLangTable.get(sstKey);
2N/A
2N/A // Get all records.
2N/A
2N/A BtreeVector bvec = regRecs.attrSort;
2N/A bvec.getAll(collector);
2N/A
2N/A }
2N/A
2N/A return records.elements();
2N/A }
2N/A
2N/A
2N/A /**
2N/A * Dump the service store to the log.
2N/A *
2N/A */
2N/A
2N/A synchronized public void dumpServiceStore() {
2N/A
2N/A SLPConfig conf = SLPConfig.getSLPConfig();
2N/A
2N/A conf.writeLogLine("ssim_dump_start",
2N/A new Object[] {this});
2N/A
2N/A Enumeration keys = scopeTypeLangTable.keys();
2N/A
2N/A while (keys.hasMoreElements()) {
2N/A String sstKey = (String)keys.nextElement();
2N/A STLRecord regRec = (STLRecord)scopeTypeLangTable.get(sstKey);
2N/A
2N/A // If the service type is abstract, then skip it. It will be
2N/A // displayed when the concrete type is.
2N/A
2N/A if (regRec.isAbstract) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // Get all records.
2N/A
2N/A BtreeVector bvec = regRec.attrSort;
2N/A Vector vReturns = new Vector();
2N/A BVCollector collector = new AllBVCollector(vReturns);
2N/A
2N/A bvec.getAll(collector);
2N/A
2N/A // Now write them out.
2N/A
2N/A int i, n = vReturns.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServiceRecordInMemory rec =
2N/A (ServiceRecordInMemory)vReturns.elementAt(i);
2N/A
2N/A writeRecordToLog(conf, rec);
2N/A }
2N/A }
2N/A
2N/A conf.writeLog("ssim_dump_end",
2N/A new Object[] {this});
2N/A }
2N/A
2N/A //
2N/A // Protected/private methods.
2N/A //
2N/A
2N/A // Register the record without any preliminaries. We assume that
2N/A // any old records have been removed and merged into this one,
2N/A // as necessary.
2N/A
2N/A private void registerInternal(ServiceRecordInMemory rec) {
2N/A
2N/A ServiceURL surl = rec.getServiceURL();
2N/A ServiceType type = surl.getServiceType();
2N/A String serviceType = type.toString();
2N/A String abstractTypeName = type.getAbstractTypeName();
2N/A Locale locale = rec.getLocale();
2N/A String lang = locale.getLanguage();
2N/A Vector scopes = rec.getScopes();
2N/A
2N/A // Make one age out queue entry. It will go into
2N/A // all scopes, but that's OK.
2N/A
2N/A List ageOutElem = addToAgeOutQueue(rec);
2N/A
2N/A // Go through all scopes.
2N/A
2N/A int i, n = scopes.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A
2N/A // Initialize the urltable list vector for this URL.
2N/A
2N/A Vector listVec =
2N/A initializeURLScopeLangTableVector(surl, scope, lang);
2N/A
2N/A // Add to scope/type/lang table.
2N/A
2N/A addRecordToScopeTypeLangTable(scope,
2N/A serviceType,
2N/A lang,
2N/A false,
2N/A rec,
2N/A listVec);
2N/A
2N/A // Add a new service type/scope record for this locale.
2N/A
2N/A addTypeLocale(serviceType, scope, lang);
2N/A
2N/A // Add ageOut record, so that it gets deleted when
2N/A // the record does.
2N/A
2N/A listVec.addElement(ageOutElem);
2N/A
2N/A // If the type is an abstract type, then add
2N/A // separate records.
2N/A
2N/A if (type.isAbstractType()) {
2N/A addRecordToScopeTypeLangTable(scope,
2N/A abstractTypeName,
2N/A lang,
2N/A true,
2N/A rec,
2N/A listVec);
2N/A addTypeLocale(abstractTypeName, scope, lang);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Create a urlScopeLangTable record for this URL.
2N/A
2N/A private Vector
2N/A initializeURLScopeLangTableVector(ServiceURL url,
2N/A String scope,
2N/A String lang) {
2N/A
2N/A // Get scope level, creating if new.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(url.toString());
2N/A
2N/A if (scopeLevel == null) {
2N/A scopeLevel = new Hashtable();
2N/A urlScopeLangTable.put(url.toString(), scopeLevel);
2N/A
2N/A }
2N/A
2N/A // Get lang level, creating if new.
2N/A
2N/A Hashtable langLevel =
2N/A (Hashtable)scopeLevel.get(scope);
2N/A
2N/A if (langLevel == null) {
2N/A langLevel = new Hashtable();
2N/A scopeLevel.put(scope, langLevel);
2N/A
2N/A }
2N/A
2N/A // Check whether there's anything already there.
2N/A // Bug if so.
2N/A
2N/A Assert.slpassert(langLevel.get(lang) == null,
2N/A "ssim_url_lang_botch",
2N/A new Object[] {lang,
2N/A url,
2N/A scope});
2N/A
2N/A // Add a new list vector, and return it.
2N/A
2N/A Vector listVec = new Vector();
2N/A
2N/A langLevel.put(lang, listVec);
2N/A
2N/A return listVec;
2N/A
2N/A }
2N/A
2N/A // Add a record to the scope/type/language table.
2N/A
2N/A private void
2N/A addRecordToScopeTypeLangTable(String scope,
2N/A String serviceType,
2N/A String lang,
2N/A boolean isAbstract,
2N/A ServiceRecordInMemory rec,
2N/A Vector listVec) {
2N/A
2N/A // Make key for scope/type/language table.
2N/A
2N/A String stlKey = makeScopeTypeLangKey(scope, serviceType, lang);
2N/A
2N/A // Get record for scope/type/lang.
2N/A
2N/A STLRecord trec = (STLRecord)scopeTypeLangTable.get(stlKey);
2N/A
2N/A // If it's not there, make it.
2N/A
2N/A if (trec == null) {
2N/A trec = new STLRecord(isAbstract);
2N/A scopeTypeLangTable.put(stlKey, trec);
2N/A
2N/A }
2N/A
2N/A // Otherwise, add record to all.
2N/A
2N/A addRecordToAttrValueSort(trec.attrValueSort, rec, listVec);
2N/A addRecordToAttrSort(trec.attrSort, rec, listVec);
2N/A
2N/A }
2N/A
2N/A // Add a new record into the attr value table.
2N/A
2N/A private void
2N/A addRecordToAttrValueSort(Hashtable table,
2N/A ServiceRecordInMemory rec,
2N/A Vector listVec) {
2N/A
2N/A Vector attrList = rec.getAttrList();
2N/A int i, n = attrList.size();
2N/A
2N/A // Go through the attribute list.
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)attrList.elementAt(i);
2N/A AttributeString tag = attr.idPattern;
2N/A Vector values = attr.values;
2N/A
2N/A // If a type table record exists, use it. Otherwise,
2N/A // create a newly initialized one.
2N/A
2N/A Hashtable ttable = (Hashtable)table.get(tag);
2N/A
2N/A if (ttable == null) {
2N/A ttable = makeAttrTypeTable();
2N/A table.put(tag, ttable);
2N/A
2N/A }
2N/A
2N/A // Get the class of values.
2N/A
2N/A String typeKey = null;
2N/A
2N/A if (values == null) {
2N/A
2N/A // We're done, since there are no attributes to add.
2N/A
2N/A continue;
2N/A
2N/A } else {
2N/A Object val = values.elementAt(0);
2N/A
2N/A typeKey = val.getClass().getName();
2N/A }
2N/A
2N/A // Get the BtreeVector.
2N/A
2N/A BtreeVector bvec =
2N/A (BtreeVector)ttable.get(typeKey);
2N/A
2N/A // Insert a record for each value.
2N/A
2N/A int j, m = values.size();
2N/A
2N/A for (j = 0; j < m; j++) {
2N/A List elem = bvec.add(values.elementAt(j), rec);
2N/A
2N/A // Put the element into the deletion table.
2N/A
2N/A listVec.addElement(elem);
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Return a newly initialized attribute type table. It will
2N/A // have a hash for each allowed type, with a new BtreeVector
2N/A // attached.
2N/A
2N/A private Hashtable makeAttrTypeTable() {
2N/A
2N/A Hashtable ret = new Hashtable();
2N/A
2N/A ret.put(INTEGER_TYPE, new BtreeVector());
2N/A ret.put(ATTRIBUTE_STRING_TYPE, new BtreeVector());
2N/A ret.put(BOOLEAN_TYPE, new BtreeVector());
2N/A ret.put(OPAQUE_TYPE, new BtreeVector());
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A // Add a new record into the attrs table.
2N/A
2N/A private void
2N/A addRecordToAttrSort(BtreeVector table,
2N/A ServiceRecordInMemory rec,
2N/A Vector listVec) {
2N/A
2N/A Vector attrList = rec.getAttrList();
2N/A int i, n = attrList.size();
2N/A
2N/A // If no attributes, then add with empty string as
2N/A // the attribute tag.
2N/A
2N/A if (n <= 0) {
2N/A List elem =
2N/A table.add(new AttributeString("", rec.getLocale()), rec);
2N/A
2N/A listVec.addElement(elem);
2N/A
2N/A return;
2N/A }
2N/A
2N/A // Iterate through the attribute list, adding to the
2N/A // BtreeVector with attribute as the sort key.
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)attrList.elementAt(i);
2N/A
2N/A List elem = table.add(attr.idPattern, rec);
2N/A
2N/A // Save for deletion.
2N/A
2N/A listVec.addElement(elem);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Add a record to the ageOut queue.
2N/A
2N/A private List addToAgeOutQueue(ServiceRecordInMemory rec) {
2N/A
2N/A Long exTime = new Long(rec.getExpirationTime());
2N/A return ageOutQueue.add(exTime, rec);
2N/A
2N/A }
2N/A
2N/A // Remove the URL record from the database.
2N/A
2N/A private void
2N/A deregisterInternal(ServiceURL url, Vector scopes, String lang) {
2N/A
2N/A ServiceType type = url.getServiceType();
2N/A
2N/A // To deregister, we only need to find the Vector of List objects
2N/A // containing the places where this registration is hooked into
2N/A // lists and unhook them. Garbage collection of other structures
2N/A // is handled during insertion or in deregisterTypeLocale(),
2N/A // if there are no more registrations at all.
2N/A
2N/A // Find the scope table..
2N/A
2N/A Hashtable scopeLangTable =
2N/A (Hashtable)urlScopeLangTable.get(url.toString());
2N/A
2N/A // If it's not there, then maybe not registered.
2N/A
2N/A if (scopeLangTable == null) {
2N/A return;
2N/A
2N/A }
2N/A
2N/A // For each scope, find the lang table.
2N/A
2N/A int i, n = scopes.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A
2N/A Hashtable langTable = (Hashtable)scopeLangTable.get(scope);
2N/A
2N/A if (langTable == null) {
2N/A continue;
2N/A }
2N/A
2N/A // If the locale is non-null, then just deregister from this
2N/A // locale.
2N/A
2N/A if (lang != null) {
2N/A deregisterFromLocale(langTable, lang);
2N/A
2N/A // Record the deletion in the scope/type table, and
2N/A // also the number of regs table.
2N/A
2N/A deleteTypeLocale(type.toString(), scope, lang);
2N/A
2N/A // Check for abstract type as well.
2N/A
2N/A if (type.isAbstractType()) {
2N/A deleteTypeLocale(type.getAbstractTypeName(), scope, lang);
2N/A
2N/A }
2N/A
2N/A } else {
2N/A
2N/A // Otherwise, deregister all languages.
2N/A
2N/A Enumeration en = langTable.keys();
2N/A
2N/A while (en.hasMoreElements()) {
2N/A lang = (String)en.nextElement();
2N/A
2N/A deregisterFromLocale(langTable, lang);
2N/A
2N/A // Record the deletion in the scope/type table, and
2N/A // also the number of regs table.
2N/A
2N/A deleteTypeLocale(type.toString(), scope, lang);
2N/A
2N/A // Check for abstract type as well.
2N/A
2N/A if (type.isAbstractType()) {
2N/A deleteTypeLocale(type.getAbstractTypeName(),
2N/A scope,
2N/A lang);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // If the table is empty, then remove the lang table.
2N/A
2N/A if (langTable.size() <= 0) {
2N/A scopeLangTable.remove(scope);
2N/A
2N/A }
2N/A }
2N/A
2N/A // If all languages were deleted, delete the
2N/A // urlScopeLangTable record. Other GC handled in
2N/A // deleteTypeLocale().
2N/A
2N/A if (scopeLangTable.size() <= 0) {
2N/A urlScopeLangTable.remove(url.toString());
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // Deregister a single locale from the language table.
2N/A
2N/A private void deregisterFromLocale(Hashtable langTable, String lang) {
2N/A
2N/A // Get the Vector containing the list of registrations.
2N/A
2N/A Vector regList = (Vector)langTable.get(lang);
2N/A
2N/A Assert.slpassert(regList != null,
2N/A "ssim_null_reg_vector",
2N/A new Object[] {lang});
2N/A
2N/A // Walk down the list of registrations and unhook them from
2N/A // their respective lists.
2N/A
2N/A int i, n = regList.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A List elem = (List)regList.elementAt(i);
2N/A
2N/A elem.delete();
2N/A
2N/A }
2N/A
2N/A // Remove the locale record.
2N/A
2N/A langTable.remove(lang);
2N/A }
2N/A
2N/A // Find an existing record matching the URL by searching in all scopes.
2N/A // The record will be the same for all scopes in the same language.
2N/A // If locale is null, return any. If there are none, return null.
2N/A
2N/A private ServiceRecordInMemory
2N/A findExistingRecord(ServiceURL surl, Vector scopes, String lang) {
2N/A
2N/A ServiceRecordInMemory rec = null;
2N/A
2N/A // Look in urlScopeLangTable.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(surl.toString());
2N/A
2N/A if (scopeLevel != null) {
2N/A
2N/A // If scopes is null, then perform the search for all
2N/A // scopes in the table. Otherwise perform it for
2N/A // all scopes incoming.
2N/A
2N/A Enumeration en = null;
2N/A
2N/A if (scopes == null) {
2N/A en = scopeLevel.keys();
2N/A
2N/A } else {
2N/A en = scopes.elements();
2N/A
2N/A }
2N/A
2N/A while (en.hasMoreElements()) {
2N/A String scope = (String)en.nextElement();
2N/A Hashtable langLevel = (Hashtable)scopeLevel.get(scope);
2N/A
2N/A // If no langLevel table, continue searching.
2N/A
2N/A if (langLevel == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A Vector listVec = null;
2N/A
2N/A // Use lang tag if we have it, otherwise, pick arbitrary.
2N/A
2N/A if (lang != null) {
2N/A listVec = (Vector)langLevel.get(lang);
2N/A
2N/A } else {
2N/A Enumeration llen = langLevel.elements();
2N/A
2N/A listVec = (Vector)llen.nextElement();
2N/A
2N/A }
2N/A
2N/A // If none for this locale, try the next scope.
2N/A
2N/A if (listVec == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // Select out the record.
2N/A
2N/A List elem = (List)listVec.elementAt(0);
2N/A
2N/A rec = elem.record;
2N/A break;
2N/A
2N/A }
2N/A }
2N/A
2N/A return rec;
2N/A }
2N/A
2N/A // Find attributes matching the record and place the matching attributes
2N/A // into the vector. Use the hashtable for collation.
2N/A
2N/A static void
2N/A findMatchingAttributes(ServiceRecordInMemory rec,
2N/A Vector attrTags,
2N/A Hashtable ht,
2N/A Vector ret)
2N/A throws ServiceLocationException {
2N/A
2N/A int len = attrTags.size();
2N/A Vector attrList = rec.getAttrList();
2N/A
2N/A // For each attribute, go through the tag vector If an attribute
2N/A // matches, merge it into the return vector.
2N/A
2N/A int i, n = attrList.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)attrList.elementAt(i);
2N/A
2N/A // All attributes match if the pattern vector is
2N/A // empty.
2N/A
2N/A if (len <= 0) {
2N/A saveValueIfMatch(attr, null, ht, ret);
2N/A
2N/A } else {
2N/A
2N/A // Check each pattern against the attribute id.
2N/A
2N/A int j;
2N/A
2N/A for (j = 0; j < len; j++) {
2N/A AttributePattern attrTag =
2N/A (AttributePattern)attrTags.elementAt(j);
2N/A
2N/A saveValueIfMatch(attr, attrTag, ht, ret);
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Check the attribute against the pattern. If the pattern is null,
2N/A // then match occurs. Merge the attribute into the vector
2N/A // if match.
2N/A
2N/A static private void saveValueIfMatch(ServerAttribute attr,
2N/A AttributePattern attrTag,
2N/A Hashtable ht,
2N/A Vector ret)
2N/A throws ServiceLocationException {
2N/A
2N/A AttributeString id = attr.idPattern;
2N/A
2N/A // We save the attribute value if either
2N/A // the pattern is null or it matches the attribute id.
2N/A
2N/A if (attrTag == null || attrTag.match(id)) {
2N/A
2N/A Vector values = attr.getValues();
2N/A
2N/A // Create new values vector so record copy isn't
2N/A // modified.
2N/A
2N/A if (values != null) {
2N/A values = (Vector)values.clone();
2N/A
2N/A }
2N/A
2N/A // Create new attribute so record copy isn't
2N/A // modified.
2N/A
2N/A ServiceLocationAttribute nattr =
2N/A new ServiceLocationAttribute(attr.getId(), values);
2N/A
2N/A // Merge duplicate attributes into vector.
2N/A
2N/A ServiceLocationAttribute.mergeDuplicateAttributes(nattr,
2N/A ht,
2N/A ret,
2N/A true);
2N/A }
2N/A }
2N/A
2N/A // Check whether the incoming scopes are the same as existing
2N/A // scopes.
2N/A
2N/A private void
2N/A checkScopeStatus(ServiceURL surl,
2N/A Vector scopes,
2N/A short errCode)
2N/A throws ServiceLocationException {
2N/A
2N/A // Drill down in the urlScopeLangTable table.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(surl.toString());
2N/A
2N/A if (scopeLevel == null) {
2N/A return; // not yet registered...
2N/A
2N/A }
2N/A
2N/A // We need to have exactly the same scopes as in
2N/A // the registration.
2N/A
2N/A int i, n = scopes.size();
2N/A boolean ok = true;
2N/A
2N/A if (n != scopeLevel.size()) {
2N/A ok = false;
2N/A
2N/A } else {
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A if (scopeLevel.get(scopes.elementAt(i)) == null) {
2N/A ok = false;
2N/A break;
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (!ok) {
2N/A throw
2N/A new ServiceLocationException(errCode,
2N/A "ssim_scope_mis",
2N/A new Object[0]);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Check whether an existing nonservice URL is registered under
2N/A // a different service type.
2N/A
2N/A private void checkForExistingUnderOtherServiceType(ServiceURL url,
2N/A Vector scopes)
2N/A throws ServiceLocationException {
2N/A
2N/A // Drill down in the urlScopeLangTable table.
2N/A
2N/A Hashtable scopeLevel =
2N/A (Hashtable)urlScopeLangTable.get(url.toString());
2N/A
2N/A if (scopeLevel == null) {
2N/A return; // not yet registered.
2N/A
2N/A }
2N/A
2N/A // Get hashtable of locale records under scopes. Any scope
2N/A // will do.
2N/A
2N/A Object scope = scopes.elementAt(0);
2N/A
2N/A Hashtable localeLevel = (Hashtable)scopeLevel.get(scope);
2N/A
2N/A Assert.slpassert(localeLevel != null,
2N/A "ssim_null_lang_table",
2N/A new Object[] {scope});
2N/A
2N/A // Get a record from any locale.
2N/A
2N/A Enumeration en = localeLevel.elements();
2N/A
2N/A Assert.slpassert(en.hasMoreElements(),
2N/A "ssim_empty_lang_table",
2N/A new Object[] {scope});
2N/A
2N/A // Get vector of registrations.
2N/A
2N/A Vector vec = (Vector)en.nextElement();
2N/A
2N/A Assert.slpassert(vec.size() > 0,
2N/A "ssim_empty_reg_vector",
2N/A new Object[] {scope});
2N/A
2N/A List elem = (List)vec.elementAt(0);
2N/A
2N/A // OK, now check the registration.
2N/A
2N/A ServiceURL recURL = elem.record.getServiceURL();
2N/A ServiceType recType = recURL.getServiceType();
2N/A
2N/A if (!recType.equals(url.getServiceType())) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.INVALID_UPDATE,
2N/A "ssim_st_already",
2N/A new Object[0]);
2N/A }
2N/A }
2N/A
2N/A // Merge old record into new record.
2N/A
2N/A final private void mergeOldRecordIntoNew(ServiceRecordInMemory oldRec,
2N/A ServiceRecordInMemory newRec)
2N/A throws ServiceLocationException {
2N/A
2N/A Vector newAttrs = newRec.getAttrList();
2N/A Vector oldAttrs = oldRec.getAttrList();
2N/A Hashtable ht = new Hashtable();
2N/A
2N/A // Charge up the hashtable with the new attributes.
2N/A
2N/A int i, n = newAttrs.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)newAttrs.elementAt(i);
2N/A
2N/A ht.put(attr.getId().toLowerCase(), attr);
2N/A
2N/A }
2N/A
2N/A // Merge in the old attributes.
2N/A
2N/A n = oldAttrs.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)oldAttrs.elementAt(i);
2N/A
2N/A if (ht.get(attr.getId().toLowerCase()) == null) {
2N/A newAttrs.addElement(attr);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Change the attribute vector on the rec.
2N/A
2N/A newRec.setAttrList(newAttrs);
2N/A
2N/A // Merge old scopes into new.
2N/A
2N/A Vector oldScopes = oldRec.getScopes();
2N/A Vector newScopes = newRec.getScopes();
2N/A int j, m = oldScopes.size();
2N/A
2N/A for (j = 0; j < m; j++) {
2N/A String scope = (String)oldScopes.elementAt(j);
2N/A
2N/A if (!newScopes.contains(scope)) {
2N/A newScopes.addElement(scope);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Note that we don't have to merge security because there
2N/A // will never be an incremental update to a record
2N/A // in a protected scope.
2N/A
2N/A // Change the scope vector on the rec.
2N/A
2N/A newRec.setScopes(newScopes);
2N/A
2N/A }
2N/A
2N/A // Delete attributes matching attrTags.
2N/A
2N/A private void deleteAttributes(ServiceRecordInMemory rec,
2N/A Vector attrTags)
2N/A throws ServiceLocationException {
2N/A
2N/A // For each attribute, go through the tag vector and put attributes
2N/A // that do not match the tags into the new attribute vector.
2N/A
2N/A Vector attrList = rec.getAttrList();
2N/A
2N/A // If there are no attributes for this one, then simply return.
2N/A
2N/A if (attrList.size() <= 0) {
2N/A return;
2N/A
2N/A }
2N/A
2N/A int i, n = attrList.size();
2N/A Vector newAttrList = new Vector();
2N/A int len = attrTags.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A ServerAttribute attr =
2N/A (ServerAttribute)attrList.elementAt(i);
2N/A AttributeString id = attr.idPattern;
2N/A boolean deleteIt = false;
2N/A
2N/A int j;
2N/A
2N/A // Now check the tags.
2N/A
2N/A for (j = 0; j < len; j++) {
2N/A AttributePattern attrTag =
2N/A (AttributePattern)attrTags.elementAt(j);
2N/A
2N/A // If there's a match, mark for deletion.
2N/A
2N/A if (attrTag.match(id)) {
2N/A deleteIt = true;
2N/A break;
2N/A
2N/A }
2N/A }
2N/A
2N/A if (!deleteIt) {
2N/A newAttrList.addElement(attr);
2N/A }
2N/A }
2N/A
2N/A // Replace the attribute vector in the record.
2N/A
2N/A rec.setAttrList(newAttrList);
2N/A }
2N/A
2N/A // Convert a vector of attribute tag strings to attribute pattern objects.
2N/A
2N/A private Vector stringVectorToAttributePattern(Vector tags, Locale locale)
2N/A throws ServiceLocationException {
2N/A
2N/A // Takes care of findAttributes() case where no vector.
2N/A
2N/A if (tags == null) {
2N/A return null;
2N/A
2N/A }
2N/A
2N/A Vector v = new Vector();
2N/A int i, n = tags.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String value = (String)tags.elementAt(i);
2N/A
2N/A AttributePattern tag =
2N/A new AttributePattern(value, locale);
2N/A
2N/A if (!v.contains(tag)) {
2N/A v.addElement(tag);
2N/A
2N/A }
2N/A }
2N/A
2N/A return v;
2N/A }
2N/A
2N/A //
2N/A // Output of service store to log.
2N/A //
2N/A
2N/A // Write record to config log file.
2N/A
2N/A private void
2N/A writeRecordToLog(SLPConfig conf, ServiceStore.ServiceRecord rec) {
2N/A
2N/A Locale locale = rec.getLocale();
2N/A ServiceURL surl = rec.getServiceURL();
2N/A Vector scopes = rec.getScopes();
2N/A Vector attributes = rec.getAttrList();
2N/A long exTime = rec.getExpirationTime();
2N/A Hashtable urlSig = rec.getURLSignature();
2N/A Hashtable attrSig = rec.getAttrSignature();
2N/A
2N/A conf.writeLogLine("ssim_dump_entry_start", new Object[0]);
2N/A conf.writeLogLine("ssim_dump_entry",
2N/A new Object[] {
2N/A locale,
2N/A surl.toString(),
2N/A Integer.toString(surl.getLifetime()),
2N/A Long.toString(((exTime - System.currentTimeMillis())/1000)),
2N/A surl.getServiceType(),
2N/A scopes,
2N/A attributes});
2N/A
2N/A if (urlSig != null) {
2N/A conf.writeLogLine("ssim_dump_urlsig",
2N/A new Object[] {urlSig});
2N/A
2N/A }
2N/A
2N/A if (attrSig != null) {
2N/A conf.writeLogLine("ssim_dump_attrsig",
2N/A new Object[] {
2N/A attrSig});
2N/A
2N/A }
2N/A conf.writeLogLine("ssim_entry_end", new Object[0]);
2N/A }
2N/A
2N/A //
2N/A // Utilities for dealing with service type/scope locale table.
2N/A //
2N/A
2N/A // Bump up the number of registrations for this service type, scope and
2N/A // locale.
2N/A
2N/A private void
2N/A addTypeLocale(String type, String scope, String lang) {
2N/A
2N/A String sstKey = makeScopeTypeKey(scope, type);
2N/A
2N/A // Get any existing record.
2N/A
2N/A Hashtable langTable = (Hashtable)sstLocales.get(sstKey);
2N/A
2N/A // Insert a new one if none there.
2N/A
2N/A if (langTable == null) {
2N/A langTable = new Hashtable();
2N/A
2N/A sstLocales.put(sstKey, langTable);
2N/A
2N/A }
2N/A
2N/A // Look up locale.
2N/A
2N/A Integer numRegs = (Integer)langTable.get(lang);
2N/A
2N/A // Add a new one if none there, otherwise, bump up old.
2N/A
2N/A if (numRegs == null) {
2N/A numRegs = new Integer(1);
2N/A
2N/A } else {
2N/A numRegs = new Integer(numRegs.intValue() + 1);
2N/A
2N/A }
2N/A
2N/A // Put it back.
2N/A
2N/A langTable.put(lang, numRegs);
2N/A
2N/A }
2N/A
2N/A // Bump down the number of registrations for this service type, scope,
2N/A // in all locales.
2N/A
2N/A private void deleteTypeLocale(String type, String scope, String lang) {
2N/A
2N/A String sstKey = makeScopeTypeKey(scope, type);
2N/A
2N/A // Get any existing record.
2N/A
2N/A Hashtable langTable = (Hashtable)sstLocales.get(sstKey);
2N/A
2N/A // If none there, then error. But this should have been caught
2N/A // during deletion, so it's fatal.
2N/A
2N/A Assert.slpassert(langTable != null,
2N/A "ssim_ssttable_botch",
2N/A new Object[] {
2N/A type,
2N/A scope});
2N/A
2N/A // Get the Integer object recording the number of registrations.
2N/A
2N/A Integer numRegs = (Integer)langTable.get(lang);
2N/A
2N/A Assert.slpassert(numRegs != null,
2N/A "ssim_ssttable_lang_botch",
2N/A new Object[] {
2N/A lang,
2N/A type,
2N/A scope});
2N/A
2N/A // Bump down by one, remove if zero.
2N/A
2N/A numRegs = new Integer(numRegs.intValue() - 1);
2N/A
2N/A if (numRegs.intValue() <= 0) {
2N/A langTable.remove(lang);
2N/A
2N/A if (langTable.size() <= 0) {
2N/A sstLocales.remove(sstKey);
2N/A
2N/A }
2N/A
2N/A // Garbage collection.
2N/A
2N/A // Remove records from the scopeTypeLangTable,
2N/A // since there are no registrations left for this
2N/A // type/scope/locale.
2N/A
2N/A String stlKey =
2N/A makeScopeTypeLangKey(scope, type, lang);
2N/A scopeTypeLangTable.remove(stlKey);
2N/A
2N/A } else {
2N/A
2N/A // Put it back.
2N/A
2N/A langTable.put(lang, numRegs);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Return REGS if the language is supported. Supported means that the
2N/A // there are some registrations of this service type in it or that
2N/A // there are none in any locale. Return NO_REGS if there are absolutely
2N/A // no registrations whatsoever, in any language. Return NO_REGS_IN_LOCALE
2N/A // if there are no registrations in that language but there are in
2N/A // others.
2N/A
2N/A private int
2N/A languageSupported(String type, Vector scopes, String lang) {
2N/A
2N/A // Look through scope vector.
2N/A
2N/A boolean otherLangRegs = false;
2N/A boolean sameLangRegs = false;
2N/A int i, n = scopes.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String scope = (String)scopes.elementAt(i);
2N/A String sstKey = makeScopeTypeKey(scope, type);
2N/A
2N/A // Get any existing record.
2N/A
2N/A Hashtable langTable = (Hashtable)sstLocales.get(sstKey);
2N/A
2N/A // If there are no regs, then check next scope.
2N/A
2N/A if (langTable == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A Object numRegs = langTable.get(lang);
2N/A
2N/A // Check whether there are other language regs
2N/A // or same language regs.
2N/A
2N/A if (numRegs == null) {
2N/A otherLangRegs = true;
2N/A
2N/A } else {
2N/A sameLangRegs = true;
2N/A
2N/A }
2N/A }
2N/A
2N/A // Return appropriate code.
2N/A
2N/A if (otherLangRegs == false &&
2N/A sameLangRegs == false) {
2N/A return NO_REGS;
2N/A
2N/A } else if (otherLangRegs == true &&
2N/A sameLangRegs == false) {
2N/A return NO_REGS_IN_LOCALE;
2N/A
2N/A } else {
2N/A return REGS_IN_LOCALE;
2N/A
2N/A }
2N/A }
2N/A
2N/A //
2N/A // Hash key calculations and hash table structuring.
2N/A //
2N/A
2N/A // Return a key for type and scope.
2N/A
2N/A private String makeScopeTypeKey(String scope, String type) {
2N/A return scope + "/" + type;
2N/A
2N/A }
2N/A
2N/A // Make a hash key consisting of the scope and service type.
2N/A
2N/A final private String
2N/A makeScopeTypeLangKey(String scope,
2N/A String serviceType,
2N/A String lang) {
2N/A
2N/A return scope + "/" + serviceType + "/" + lang;
2N/A }
2N/A
2N/A // Return the key's scope.
2N/A
2N/A final private String keyScope(String key) {
2N/A int idx = key.indexOf('/');
2N/A String ret = "";
2N/A
2N/A if (idx > 0) {
2N/A ret = key.substring(0, idx);
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A
2N/A // Return the key's service type/NA.
2N/A
2N/A final private String keyServiceType(String key) {
2N/A int idx = key.indexOf('/');
2N/A String ret = "";
2N/A int len = key.length();
2N/A
2N/A if (idx >= 0 && idx < len - 1) {
2N/A ret = key.substring(idx+1, len);
2N/A }
2N/A
2N/A // Parse off the final lang.
2N/A
2N/A idx = ret.indexOf('/');
2N/A
2N/A ret = ret.substring(0, idx);
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A // Return true if the record is for an abstract type.
2N/A
2N/A final private boolean isAbstractTypeRecord(String sstKey) {
2N/A STLRecord rec = (STLRecord)scopeTypeLangTable.get(sstKey);
2N/A
2N/A return rec.isAbstract;
2N/A
2N/A }
2N/A
2N/A}