1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2001,2003 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  */
26 
27 //  DATable.java:     Interface for DATables.
28 //  Author:           James Kempf
29 //  Created On:       Mon May 11 13:46:02 1998
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Mon Feb 22 15:47:37 1999
32 //  Update Count:     53
33 //
34 
35 
36 package com.sun.slp;
37 
38 /**
39  * DATable is an abstract class that provides the interface for DA
40  * and scope discovery. A variety of implementations are possible.
41  * The getDATable() method creates the right one from a subclass.
42  *
43  * @author James Kempf
44  */
45 
46 import java.util.*;
47 import java.net.*;
48 
49 abstract class DATable extends Object {
50 
51     protected static DATable daTable;
52     protected static SLPConfig conf;
53 
54     // System property naming the DATable implementation class to use.
55 
56     final static String DA_TABLE_CLASS_PROP = "sun.net.slp.DATableClass";
57 
58     // SA only scopes property.
59 
60     final static String SA_ONLY_SCOPES_PROP = "sun.net.slp.SAOnlyScopes";
61 
62     // Hashtable key for multicast scopes.
63 
64     final static String MULTICAST_KEY = "&&**^^MULTICASTxxxKEY^^**&&";
65 
66     // Hashtable key for DA equivalence classes.
67 
68     final static String UNICAST_KEY = "&&**^^UNICASTxxxKEY^^**&&";
69 
70     /**
71      * A record for all DAs supporting exactly the same set of scopes.
72      *
73      * @author James Kempf
74      */
75 
76 
77     public static class DARecord extends Object {
78 
79 	// The scopes supported.
80 
81 	Vector scopes = null;		// String scope names
82 
83 	Vector daAddresses = new Vector();  // InetAddress DA addresses
84 
85     }
86 
87     /**
88      * Return a hashtable containing two entries:
89      *
90      * MULTICAST_KEY - Vector of scopes from the incoming vector that are not
91      * supported by any known DA.
92      *
93      * UNICAST_KEY - Vector of DATable.DARecord objects containing
94      * equivalence classes of DAs that all support the same set of scopes.
95      * Only DAs supporting one or more scopes in the incoming vector
96      * are returned.
97      *
98      * Note that the equivalence classes don't necessarily mean that the
99      * set of scopes are mutually exclusive. For example, if DA1 supports
100      * scopes A, B, and C; and DA2 supports scopes C and D, then they
101      * are in separate equivalence classes even though they both support
102      * C. But if DA2 supports A, B, and C; then it is in the same equivalence
103      * class.
104      *
105      * @param scopes The scopes for which DAs are required.
106      * @return A Hashtable with the multicast scopes and DAAddresses.
107      */
108 
findDAScopes(Vector scopes)109     abstract Hashtable findDAScopes(Vector scopes)
110 	throws ServiceLocationException;
111 
112     /**
113      * Remove a DA by address.
114      *
115      * @param address The host address of the DA.
116      * @param scopes The scopes.
117      * @return True if removed, false if not.
118      */
119 
removeDA(InetAddress address, Vector scopes)120     abstract boolean removeDA(InetAddress address, Vector scopes);
121 
122     /**
123      * Return a vector of scopes that the SA or UA client should use.
124      * Note that if no DAs are around, SA adverts must be used to
125      * find SAs. We must sort through the returned DAs and apply
126      * the scope prioritization algorithm to them.
127      *
128      * @return Vector of scopes for the SA or UA client to use.
129      */
130 
findScopes()131     synchronized Vector findScopes() throws ServiceLocationException {
132 
133 	// First, get the DA addresses v.s. scopes table from the DAtable.
134 	//  This will also include DA addresses from the configuration file,
135 	//  if any. We don't filter on any scopes, since we want all of
136 	//  them. We are only interested in v2 scopes here.
137 
138 	Vector scopes = new Vector();
139 	Hashtable daRec = daTable.findDAScopes(scopes);
140 	Vector daEquivClasses = (Vector)daRec.get(UNICAST_KEY);
141 
142 	if (daEquivClasses != null) {
143 
144 	    // Go through the equivalence classes and pull out scopes.
145 
146 	    int i, n = daEquivClasses.size();
147 
148 	    for (i = 0; i < n; i++) {
149 		DARecord rec = (DARecord)daEquivClasses.elementAt(i);
150 		Vector v = rec.scopes;
151 
152 		int j, m = v.size();
153 
154 		for (j = 0; j < m; j++) {
155 		    Object s = v.elementAt(j);
156 
157 		    // Unicast scopes take precedence over multicast scopes,
158 		    //  so insert them at the beginning of the vector.
159 
160 		    if (!scopes.contains(s)) {
161 			scopes.addElement(s);
162 
163 		    }
164 		}
165 	    }
166 	}
167 
168 	return scopes;
169     }
170 
171     /**
172      * Get the right DA table implementation. The property
173      * sun.net.slp.DATableClass determines the class.
174      *
175      * @return The DATable object for this process' SLP requests.
176      */
177 
178 
getDATable()179     static DATable getDATable() {
180 
181 	// Return it right up front if we have it.
182 
183 	if (daTable != null) {
184 	    return daTable;
185 
186 	}
187 
188 	conf = SLPConfig.getSLPConfig();
189 
190 	// Link and instantiate it.
191 
192 	daTable = linkAndInstantiateFromProp();
193 
194 	return daTable;
195 
196     }
197 
198     // Link and instantiate the class in the property.
199 
linkAndInstantiateFromProp()200     static protected DATable linkAndInstantiateFromProp() {
201 
202 	// Get the property.
203 
204 	String className = System.getProperty(DA_TABLE_CLASS_PROP);
205 
206 	if (className == null) {
207 	    Assert.slpassert(false,
208 			  "no_da_table",
209 			  new Object[] {DA_TABLE_CLASS_PROP});
210 	}
211 
212 	Class tclass = null;
213 
214 	// Link the class and instantiate the object.
215 
216 	try {
217 
218 	    tclass = Class.forName(className);
219 	    daTable = (DATable)tclass.newInstance();
220 	    return daTable;
221 
222 	} catch (ClassNotFoundException ex) {
223 
224 	    Assert.slpassert(false,
225 			  "no_da_table_class",
226 			  new Object[] {className});
227 
228 	} catch (InstantiationException ex) {
229 
230 	    Assert.slpassert(false,
231 			  "instantiation_exception",
232 			  new Object[] {className});
233 
234 	} catch (IllegalAccessException ex) {
235 
236 	    Assert.slpassert(false,
237 			  "access_exception",
238 			  new Object[] {className});
239 
240 	}
241 
242 	// We won't reach this point, since the assertions will capture
243 	//  any errors and kill the program.
244 
245 	return null;
246     }
247 
248     //
249     // Utility functions for DA filtering and handling scopes.
250     //
251 
252     // Filter scopes, removing any not on the filter list if inVector is
253     //  false and removing any in the filter list if inVector is true.
254 
255     public static void
filterScopes(Vector scopes, Vector filter, boolean inVector)256 	filterScopes(Vector scopes, Vector filter, boolean inVector) {
257 
258 	int i = 0;
259 
260 	// Null or empty filter vector means that all should be accepted.
261 
262 	if (filter != null && !(filter.size() <= 0)) {
263 
264 	    while (i < scopes.size()) {
265 		String scope = (String)scopes.elementAt(i);
266 
267 		if ((!inVector && !filter.contains(scope)) ||
268 		    (inVector && filter.contains(scope))) {
269 		    scopes.removeElementAt(i);
270 
271 		} else {
272 		    i++;
273 
274 		}
275 	    }
276 	}
277     }
278 
279     // Add a new address to the equivalence class.
280 
addToEquivClass(String daaddr, Vector scopes, Vector ret)281     static boolean addToEquivClass(String daaddr, Vector scopes, Vector ret) {
282 
283 	// Create the InetAddress object.
284 
285 	InetAddress addr = null;
286 
287 	try {
288 
289 	    addr = InetAddress.getByName(daaddr);
290 
291 	} catch (UnknownHostException ex) {
292 
293 	    if (conf.traceAll()) {
294 		conf.writeLog("unknown_da_address",
295 			      new Object[] {daaddr});
296 
297 	    }
298 
299 	    return false;
300 	}
301 
302 	// Go through the existing vector.
303 
304 	int i, n = ret.size();
305 	boolean equivalent = false;
306 	DARecord rec = null;
307 
308     outer: for (i = 0; i < n && equivalent == false; i++) {
309 	rec = (DARecord)ret.elementAt(i);
310 	Vector dascopes = rec.scopes;
311 
312 	int j, m = dascopes.size();
313 
314 	for (j = 0; j < m; j++) {
315 	    String scope = (String)dascopes.elementAt(j);
316 
317 	    if (!scopes.contains(scope)) {
318 		continue outer;
319 
320 	    }
321 	}
322 
323 	equivalent = true;
324     }
325 
326 	// Make a new record if not equivalent.
327 
328 	if (!equivalent) {
329 	    rec = new DATable.DARecord();
330 	    rec.scopes = (Vector)scopes.clone();
331 
332 	    ret.addElement(rec);
333 
334 	}
335 
336 
337 	// Add to record. Optimize, by putting the local address at the
338 	//  beginning of the vector.
339 
340 	Vector interfaces = conf.getInterfaces();
341 
342 	if (interfaces.contains(addr)) {
343 	    rec.daAddresses.insertElementAt(addr, 0);
344 
345 	} else {
346 	    rec.daAddresses.addElement(addr);
347 
348 	}
349 
350 	return true;
351     }
352 
353     /**
354      * Validate the scope names. We check that they are all strings,
355      * that none are the empty string. In addition, we collate to
356      * remove duplicates, and lower case.
357      */
358 
validateScopes(Vector scopes, Locale locale)359     static void validateScopes(Vector scopes, Locale locale)
360 	throws ServiceLocationException {
361 
362 	// Check for empty vector.
363 
364 	if (scopes == null || scopes.size() <= 0) {
365 	    throw
366 		new ServiceLocationException(
367 				ServiceLocationException.PARSE_ERROR,
368 				"no_scope_vector",
369 				new Object[0]);
370 	}
371 
372 	// Check for all strings and none empty.
373 
374 	int i;
375 	Hashtable ht = new Hashtable();
376 
377 	for (i = 0; i < scopes.size(); i++) {
378 	    Object o = scopes.elementAt(i);
379 
380 	    if (!(o instanceof String)) {
381 		throw
382 		    new ServiceLocationException(
383 				ServiceLocationException.PARSE_ERROR,
384 				"non_string_element",
385 				new Object[] {scopes});
386 	    }
387 
388 	    String str = (String)o;
389 
390 	    if (str.length() <= 0) {
391 		throw
392 		    new ServiceLocationException(
393 				ServiceLocationException.PARSE_ERROR,
394 				"null_element",
395 				new Object[] {scopes});
396 	    }
397 
398 	    // Lower case, trim.
399 
400 	    str = str.toLowerCase(locale).trim();
401 
402 	    // Squeeze out spaces.
403 
404 	    StringBuffer buf = new StringBuffer();
405 	    StringTokenizer tk =
406 		new StringTokenizer(str, ServiceLocationAttribute.WHITESPACE);
407 	    String tok = null;
408 
409 	    while (tk.hasMoreTokens()) {
410 
411 		// Add a single embedded whitespace for each group found.
412 
413 		if (tok != null) {
414 		    buf.append(" ");
415 
416 		}
417 
418 		tok = tk.nextToken();
419 		buf.append(tok);
420 	    }
421 
422 	    str = buf.toString();
423 
424 	    // If it wasn't already seen, put it into the hashtable.
425 
426 	    if (ht.get(str) == null) {
427 		ht.put(str, str);
428 		scopes.setElementAt(str, i);
429 
430 	    } else {
431 		/*
432 		 *  Must decrement the index 'i' otherwise the next iteration
433 		 *  around the loop will miss the element immediately after
434 		 *  the element removed.
435 		 *
436 		 *  WARNING: Do not use 'i' again until the loop has
437 		 *           iterated as it may, after decrementing,
438 		 *           be negative.
439 		 */
440 		scopes.removeElementAt(i);
441 		i--;
442 		continue;
443 	    }
444 	}
445     }
446 
447 }
448