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