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 // SunDATable.java: A DATable implementation that uses the IPC connection. 28 // Author: James Kempf 29 // Created On: Mon May 11 15:00:23 1998 30 // Last Modified By: James Kempf 31 // Last Modified On: Thu Mar 11 15:00:58 1999 32 // Update Count: 71 33 // 34 35 package com.sun.slp; 36 37 import java.util.*; 38 import java.net.*; 39 import java.io.*; 40 41 /** 42 * The SunDATable class uses the IPC connection to obtain DA information 43 * from the SA server. By convention, the SA server answers service 44 * requests for the service type "directory-agent.sun" and including 45 * a filter for the scopes formatted as: 46 * 47 * "(&(|(scopes=<scope1>)(scopes=<scope2>)(scopes=<scope3>)...)(version=2))" 48 * 49 * with a collection of URLs that fit the request. The scope of the 50 * request is the hostname of the local machine, which will not be 51 * forwarded to any DAs. The URLs contain the 52 * DA IP address in the host field and a list of scopes in the URL 53 * part in the form of an attribute value assignment, i.e.: 54 * 55 * service:directory-agent.sun:// 199.200.200.5/scopes=eng, corp, freeb 56 * 57 * The DA/scope table is initially obtained for all scopes in the 58 * useScopes list, then refreshed periodically when the 59 * time stamp runs out. The time stamp is determined as the minimum 60 * expiration time of the service URLs. 61 * 62 * @author James Kempf 63 */ 64 65 class SunDATable extends DATable { 66 67 // The scopes identifier. 68 69 final static String SCOPES_ID = "424242SUN-TABLE-SCOPES424242"; 70 71 // DA version number. 72 73 final static String VERSION_ID = "424242SUN-TABLE-VERSION424242"; 74 75 // The scopes which reside on the SA only. 76 77 private Vector saOnlyScopes = new Vector(); 78 79 // The cached vector of DA equivalence classes. 80 81 private Vector cache = null; 82 83 // The time when the cache should be refreshed. 84 85 private long timeStamp = -1; 86 87 /** 88 * Construct a DATable. We get a cached table of accessable 89 * DAs and scopes and the SA scope names to use for querying. 90 */ 91 92 SunDATable() throws ServiceLocationException { 93 94 // Remove the common scopes from the SA scopes. This will leave 95 // the private, SA only scopes. 96 97 saOnlyScopes = conf.getSAOnlyScopes(); 98 99 Assert.slpassert(saOnlyScopes.size() > 0, 100 "no_sa_scopes", 101 new Object[0]); 102 103 // Initialize the cache. We want the scopes that can be dynamically 104 // discovered. If we have been configured with scopes, then 105 // it will be those. If not, then we want whatever we can discovery. 106 // we only want the default version for client side. 107 108 cache = getWireTable(conf.getConfiguredScopes(), Defaults.version); 109 110 } 111 112 /** 113 * Return a hashtable of DA equivalence classes and multicast 114 * scopes. Multicast scopes are stored in the special hashtable 115 * key MULTICAST_KEY. Unicast DA equivalence classes are stored 116 * under the key UNICAST_KEY. 117 * 118 * @param scopes Scope list for DAs needed. 119 * @return Hashtable with DA addresses as keys and scopes to contact 120 * them with as values. Any scopes not associated with a 121 * DA come back stored under the key MULTICAST_KEY. 122 * Unicast DA equivalence classes are stored 123 * under the key UNICAST_KEY. 124 */ 125 126 synchronized Hashtable findDAScopes(Vector scopes) 127 throws ServiceLocationException { 128 129 Hashtable ret = new Hashtable(); 130 131 Vector equivClasses = null; 132 133 // Refresh the local cache if necessary. 134 135 if (timeStamp <= System.currentTimeMillis()) { 136 Vector useScopes = conf.getConfiguredScopes(); 137 138 cache = getWireTable(useScopes, Defaults.version); 139 140 } 141 142 equivClasses = (Vector)cache.clone(); 143 int i; 144 145 // Sort through the local cache, matching against the input parameter. 146 // Collect multicast scopes. 147 148 Vector multicastScopes = (Vector)scopes.clone(); 149 150 for (i = 0; i < equivClasses.size(); i++) { 151 DARecord rec = (DARecord)equivClasses.elementAt(i); 152 Vector daScopes = (Vector)rec.scopes.clone(); 153 154 // Filter multicast scopes first. Remove any from the multicast 155 // scope list that are in daScopes. 156 157 filterScopes(multicastScopes, daScopes, true); 158 159 // Now filter daScopes. Remove any from the daScopes that are 160 // not in the input scopes. 161 162 filterScopes(daScopes, scopes, false); 163 164 // Remove this record if there are none left. 165 166 if (daScopes.size() <= 0) { 167 /* 168 * Must decrement the index 'i' otherwise the next iteration 169 * around the loop will miss the element immediately after 170 * the element removed. 171 * 172 * WARNING: Do not use 'i' again until the loop has 173 * iterated as it may, after decrementing, 174 * be negative. 175 */ 176 equivClasses.removeElementAt(i); 177 i--; 178 continue; 179 } 180 } 181 182 // Install the unicast and multicast scopes if any. 183 184 if (multicastScopes.size() > 0) { 185 ret.put(MULTICAST_KEY, multicastScopes); 186 187 } 188 189 if (equivClasses.size() > 0) { 190 ret.put(UNICAST_KEY, equivClasses); 191 192 } 193 194 return ret; 195 196 } 197 198 /** 199 * Remove a DA by address. We only remove it from the wire table 200 * so if it's down temporarily, we'll get it back again. 201 * 202 * @param address The host address of the DA. 203 * @param scopes The scopes. 204 * @return True if removed, false if not. 205 */ 206 207 boolean removeDA(InetAddress address, Vector scopes) { 208 209 // Sort through the table of equivalence classes in cache. 210 211 boolean foundit = false; 212 int i; 213 214 for (i = 0; i < cache.size(); i++) { 215 DARecord rec = (DARecord)cache.elementAt(i); 216 Vector daAddresses = rec.daAddresses; 217 218 // Ignore scopes, delete if there. Scopes will always be the 219 // ones for which this DA is to be removed. 220 221 int j, m = daAddresses.size(); 222 223 for (j = 0; j < m; j++) { 224 InetAddress daaddr = (InetAddress)daAddresses.elementAt(j); 225 226 // If they are equal, remove it, exit loop. 227 228 if (address.equals(daaddr)) { 229 foundit = true; 230 daAddresses.removeElementAt(j); 231 232 // If the cache entry is empty, remove it. 233 234 if (daAddresses.size() <= 0) { 235 cache.removeElementAt(i); 236 237 } 238 239 break; 240 241 } 242 } 243 } 244 245 return foundit; 246 247 } 248 249 // Return a vector of DARecord equivalence classes by going out to the 250 // wire for them. Merge any that are in the current process' 251 // DAAddresses property. 252 253 private Vector getWireTable(Vector scopes, int version) 254 throws ServiceLocationException { 255 256 Vector ret = new Vector(); 257 258 // Get replies from the SA server. These will be CSrvMsg replies. 259 260 CSrvMsg msg = getSrvReply(scopes, version); 261 262 // Process reply into the vector of equivalence classes by adding to 263 // to those from the preconfigured DAs. 264 265 processReply(msg, ret); 266 267 // Return vector. 268 269 return ret; 270 } 271 272 private CSrvMsg getSrvReply(Vector scopes, int version) 273 throws ServiceLocationException { 274 275 // Form the query. 276 277 StringBuffer buf = new StringBuffer(); 278 int i, n = scopes.size(); 279 280 for (i = 0; i < n; i++) { 281 buf.append("("); 282 buf.append(SCOPES_ID); 283 buf.append("="); 284 buf.append((String)scopes.elementAt(i)); 285 buf.append(")"); 286 } 287 288 // Add logical disjunction if there is more than one scope. 289 290 if (i > 1) { 291 buf.insert(0, "(|"); 292 buf.append(")"); 293 294 } 295 296 // Add version number restriction. 297 298 if (i > 0) { 299 buf.insert(0, "(&"); 300 301 } 302 303 buf.append("("); 304 buf.append(VERSION_ID); 305 buf.append("="); 306 buf.append((new Integer(version)).toString()); 307 buf.append(")"); 308 309 // Add closing paren if there were any scopes. 310 311 if (i > 0) { 312 buf.append(")"); 313 314 } 315 316 // Create the message object. Note that if scope vector is 317 // empty, the query is the null string, and so all DAs 318 // will be returned. 319 320 CSrvMsg msg = new CSrvMsg(Defaults.locale, 321 Defaults.SUN_DA_SERVICE_TYPE, 322 saOnlyScopes, 323 buf.toString()); 324 325 // Send it down the pipe to the IPC process. It's a bad bug 326 // if the reply comes back as not a CSrvMsg. 327 328 SrvLocMsg rply = 329 Transact.transactTCPMsg(conf.getLoopback(), msg, true); 330 331 // Check error code. 332 333 if (rply == null || 334 rply.getErrorCode() != ServiceLocationException.OK) { 335 short errCode = 336 (rply == null ? 337 ServiceLocationException.INTERNAL_SYSTEM_ERROR : 338 rply.getErrorCode()); 339 throw 340 new ServiceLocationException(errCode, 341 "loopback_error", 342 new Object[] { 343 new Short(errCode)}); 344 345 } 346 347 return (CSrvMsg)rply; 348 } 349 350 // Process CSrvMsg reply into DA equivalence class vector 351 352 private void processReply(CSrvMsg msg, Vector ret) 353 throws ServiceLocationException { 354 355 int shortTimer = Integer.MAX_VALUE; 356 357 // Get the URLs. 358 359 Vector serviceURLs = msg.serviceURLs; 360 361 // Process each service URL. 362 363 int i, n = serviceURLs.size(); 364 365 for (i = 0; i < n; i++) { 366 ServiceURL url = (ServiceURL)serviceURLs.elementAt(i); 367 368 // If the time to live is less than the current minimum, 369 // save it. 370 371 int lifetime = url.getLifetime(); 372 373 if (lifetime < shortTimer) { 374 shortTimer = lifetime; 375 376 } 377 378 // Get the host name and URL part. 379 380 String daaddr = url.getHost(); 381 String urlpart = url.getURLPath(); 382 383 // Parse URL part into scope list. Be 384 // sure not to include the initial `/' in the parse. 385 386 StringTokenizer tk = 387 new StringTokenizer(urlpart.substring(1, 388 urlpart.length()), 389 ";"); 390 Vector daScopes = null; 391 392 while (tk.hasMoreElements()) { 393 String attrExp = tk.nextToken(); 394 395 // Convert to an SLP attribute. 396 397 ServiceLocationAttribute attr = 398 new ServiceLocationAttribute("(" + attrExp + ")", false); 399 400 // Depending on the attribute id, do something. 401 402 String id = attr.getId(); 403 Vector vals = attr.getValues(); 404 405 if (id.equals(SCOPES_ID)) { 406 daScopes = vals; 407 408 } else { 409 throw 410 new ServiceLocationException( 411 ServiceLocationException.PARSE_ERROR, 412 "loopback_parse_error", 413 new Object[] {url}); 414 } 415 } 416 417 // Add it to the equivalence class. 418 419 addToEquivClass(daaddr, daScopes, ret); 420 421 } 422 423 // Reset the timestamp. 424 425 timeStamp = System.currentTimeMillis() + (long)(shortTimer * 1000); 426 427 } 428 429 } 430