/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ // DATable.java: Interface for DATables. // Author: James Kempf // Created On: Mon May 11 13:46:02 1998 // Last Modified By: James Kempf // Last Modified On: Mon Feb 22 15:47:37 1999 // Update Count: 53 // package com.sun.slp; /** * DATable is an abstract class that provides the interface for DA * and scope discovery. A variety of implementations are possible. * The getDATable() method creates the right one from a subclass. * * @author James Kempf */ import java.util.*; import java.net.*; abstract class DATable extends Object { protected static DATable daTable; protected static SLPConfig conf; // System property naming the DATable implementation class to use. final static String DA_TABLE_CLASS_PROP = "sun.net.slp.DATableClass"; // SA only scopes property. final static String SA_ONLY_SCOPES_PROP = "sun.net.slp.SAOnlyScopes"; // Hashtable key for multicast scopes. final static String MULTICAST_KEY = "&&**^^MULTICASTxxxKEY^^**&&"; // Hashtable key for DA equivalence classes. final static String UNICAST_KEY = "&&**^^UNICASTxxxKEY^^**&&"; /** * A record for all DAs supporting exactly the same set of scopes. * * @author James Kempf */ public static class DARecord extends Object { // The scopes supported. Vector scopes = null; // String scope names Vector daAddresses = new Vector(); // InetAddress DA addresses } /** * Return a hashtable containing two entries: * * MULTICAST_KEY - Vector of scopes from the incoming vector that are not * supported by any known DA. * * UNICAST_KEY - Vector of DATable.DARecord objects containing * equivalence classes of DAs that all support the same set of scopes. * Only DAs supporting one or more scopes in the incoming vector * are returned. * * Note that the equivalence classes don't necessarily mean that the * set of scopes are mutually exclusive. For example, if DA1 supports * scopes A, B, and C; and DA2 supports scopes C and D, then they * are in separate equivalence classes even though they both support * C. But if DA2 supports A, B, and C; then it is in the same equivalence * class. * * @param scopes The scopes for which DAs are required. * @return A Hashtable with the multicast scopes and DAAddresses. */ abstract Hashtable findDAScopes(Vector scopes) throws ServiceLocationException; /** * Remove a DA by address. * * @param address The host address of the DA. * @param scopes The scopes. * @return True if removed, false if not. */ abstract boolean removeDA(InetAddress address, Vector scopes); /** * Return a vector of scopes that the SA or UA client should use. * Note that if no DAs are around, SA adverts must be used to * find SAs. We must sort through the returned DAs and apply * the scope prioritization algorithm to them. * * @return Vector of scopes for the SA or UA client to use. */ synchronized Vector findScopes() throws ServiceLocationException { // First, get the DA addresses v.s. scopes table from the DAtable. // This will also include DA addresses from the configuration file, // if any. We don't filter on any scopes, since we want all of // them. We are only interested in v2 scopes here. Vector scopes = new Vector(); Hashtable daRec = daTable.findDAScopes(scopes); Vector daEquivClasses = (Vector)daRec.get(UNICAST_KEY); if (daEquivClasses != null) { // Go through the equivalence classes and pull out scopes. int i, n = daEquivClasses.size(); for (i = 0; i < n; i++) { DARecord rec = (DARecord)daEquivClasses.elementAt(i); Vector v = rec.scopes; int j, m = v.size(); for (j = 0; j < m; j++) { Object s = v.elementAt(j); // Unicast scopes take precedence over multicast scopes, // so insert them at the beginning of the vector. if (!scopes.contains(s)) { scopes.addElement(s); } } } } return scopes; } /** * Get the right DA table implementation. The property * sun.net.slp.DATableClass determines the class. * * @return The DATable object for this process' SLP requests. */ static DATable getDATable() { // Return it right up front if we have it. if (daTable != null) { return daTable; } conf = SLPConfig.getSLPConfig(); // Link and instantiate it. daTable = linkAndInstantiateFromProp(); return daTable; } // Link and instantiate the class in the property. static protected DATable linkAndInstantiateFromProp() { // Get the property. String className = System.getProperty(DA_TABLE_CLASS_PROP); if (className == null) { Assert.slpassert(false, "no_da_table", new Object[] {DA_TABLE_CLASS_PROP}); } Class tclass = null; // Link the class and instantiate the object. try { tclass = Class.forName(className); daTable = (DATable)tclass.newInstance(); return daTable; } catch (ClassNotFoundException ex) { Assert.slpassert(false, "no_da_table_class", new Object[] {className}); } catch (InstantiationException ex) { Assert.slpassert(false, "instantiation_exception", new Object[] {className}); } catch (IllegalAccessException ex) { Assert.slpassert(false, "access_exception", new Object[] {className}); } // We won't reach this point, since the assertions will capture // any errors and kill the program. return null; } // // Utility functions for DA filtering and handling scopes. // // Filter scopes, removing any not on the filter list if inVector is // false and removing any in the filter list if inVector is true. public static void filterScopes(Vector scopes, Vector filter, boolean inVector) { int i = 0; // Null or empty filter vector means that all should be accepted. if (filter != null && !(filter.size() <= 0)) { while (i < scopes.size()) { String scope = (String)scopes.elementAt(i); if ((!inVector && !filter.contains(scope)) || (inVector && filter.contains(scope))) { scopes.removeElementAt(i); } else { i++; } } } } // Add a new address to the equivalence class. static boolean addToEquivClass(String daaddr, Vector scopes, Vector ret) { // Create the InetAddress object. InetAddress addr = null; try { addr = InetAddress.getByName(daaddr); } catch (UnknownHostException ex) { if (conf.traceAll()) { conf.writeLog("unknown_da_address", new Object[] {daaddr}); } return false; } // Go through the existing vector. int i, n = ret.size(); boolean equivalent = false; DARecord rec = null; outer: for (i = 0; i < n && equivalent == false; i++) { rec = (DARecord)ret.elementAt(i); Vector dascopes = rec.scopes; int j, m = dascopes.size(); for (j = 0; j < m; j++) { String scope = (String)dascopes.elementAt(j); if (!scopes.contains(scope)) { continue outer; } } equivalent = true; } // Make a new record if not equivalent. if (!equivalent) { rec = new DATable.DARecord(); rec.scopes = (Vector)scopes.clone(); ret.addElement(rec); } // Add to record. Optimize, by putting the local address at the // beginning of the vector. Vector interfaces = conf.getInterfaces(); if (interfaces.contains(addr)) { rec.daAddresses.insertElementAt(addr, 0); } else { rec.daAddresses.addElement(addr); } return true; } /** * Validate the scope names. We check that they are all strings, * that none are the empty string. In addition, we collate to * remove duplicates, and lower case. */ static void validateScopes(Vector scopes, Locale locale) throws ServiceLocationException { // Check for empty vector. if (scopes == null || scopes.size() <= 0) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "no_scope_vector", new Object[0]); } // Check for all strings and none empty. int i; Hashtable ht = new Hashtable(); for (i = 0; i < scopes.size(); i++) { Object o = scopes.elementAt(i); if (!(o instanceof String)) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "non_string_element", new Object[] {scopes}); } String str = (String)o; if (str.length() <= 0) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "null_element", new Object[] {scopes}); } // Lower case, trim. str = str.toLowerCase(locale).trim(); // Squeeze out spaces. StringBuffer buf = new StringBuffer(); StringTokenizer tk = new StringTokenizer(str, ServiceLocationAttribute.WHITESPACE); String tok = null; while (tk.hasMoreTokens()) { // Add a single embedded whitespace for each group found. if (tok != null) { buf.append(" "); } tok = tk.nextToken(); buf.append(tok); } str = buf.toString(); // If it wasn't already seen, put it into the hashtable. if (ht.get(str) == null) { ht.put(str, str); scopes.setElementAt(str, i); } else { /* * Must decrement the index 'i' otherwise the next iteration * around the loop will miss the element immediately after * the element removed. * * WARNING: Do not use 'i' again until the loop has * iterated as it may, after decrementing, * be negative. */ scopes.removeElementAt(i); i--; continue; } } } }