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