10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7298SMark.J.Nelson@Sun.COM * Common Development and Distribution License (the "License"). 6*7298SMark.J.Nelson@Sun.COM * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 220Sstevel@tonic-gate * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate // SunDATable.java: A DATable implementation that uses the IPC connection. 280Sstevel@tonic-gate // Author: James Kempf 290Sstevel@tonic-gate // Created On: Mon May 11 15:00:23 1998 300Sstevel@tonic-gate // Last Modified By: James Kempf 310Sstevel@tonic-gate // Last Modified On: Thu Mar 11 15:00:58 1999 320Sstevel@tonic-gate // Update Count: 71 330Sstevel@tonic-gate // 340Sstevel@tonic-gate 350Sstevel@tonic-gate package com.sun.slp; 360Sstevel@tonic-gate 370Sstevel@tonic-gate import java.util.*; 380Sstevel@tonic-gate import java.net.*; 390Sstevel@tonic-gate import java.io.*; 400Sstevel@tonic-gate 410Sstevel@tonic-gate /** 420Sstevel@tonic-gate * The SunDATable class uses the IPC connection to obtain DA information 430Sstevel@tonic-gate * from the SA server. By convention, the SA server answers service 440Sstevel@tonic-gate * requests for the service type "directory-agent.sun" and including 450Sstevel@tonic-gate * a filter for the scopes formatted as: 460Sstevel@tonic-gate * 470Sstevel@tonic-gate * "(&(|(scopes=<scope1>)(scopes=<scope2>)(scopes=<scope3>)...)(version=2))" 480Sstevel@tonic-gate * 490Sstevel@tonic-gate * with a collection of URLs that fit the request. The scope of the 500Sstevel@tonic-gate * request is the hostname of the local machine, which will not be 510Sstevel@tonic-gate * forwarded to any DAs. The URLs contain the 520Sstevel@tonic-gate * DA IP address in the host field and a list of scopes in the URL 530Sstevel@tonic-gate * part in the form of an attribute value assignment, i.e.: 540Sstevel@tonic-gate * 550Sstevel@tonic-gate * service:directory-agent.sun:// 199.200.200.5/scopes=eng, corp, freeb 560Sstevel@tonic-gate * 570Sstevel@tonic-gate * The DA/scope table is initially obtained for all scopes in the 580Sstevel@tonic-gate * useScopes list, then refreshed periodically when the 590Sstevel@tonic-gate * time stamp runs out. The time stamp is determined as the minimum 600Sstevel@tonic-gate * expiration time of the service URLs. 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * @author James Kempf 630Sstevel@tonic-gate */ 640Sstevel@tonic-gate 650Sstevel@tonic-gate class SunDATable extends DATable { 660Sstevel@tonic-gate 670Sstevel@tonic-gate // The scopes identifier. 680Sstevel@tonic-gate 690Sstevel@tonic-gate final static String SCOPES_ID = "424242SUN-TABLE-SCOPES424242"; 700Sstevel@tonic-gate 710Sstevel@tonic-gate // DA version number. 720Sstevel@tonic-gate 730Sstevel@tonic-gate final static String VERSION_ID = "424242SUN-TABLE-VERSION424242"; 740Sstevel@tonic-gate 750Sstevel@tonic-gate // The scopes which reside on the SA only. 760Sstevel@tonic-gate 770Sstevel@tonic-gate private Vector saOnlyScopes = new Vector(); 780Sstevel@tonic-gate 790Sstevel@tonic-gate // The cached vector of DA equivalence classes. 800Sstevel@tonic-gate 810Sstevel@tonic-gate private Vector cache = null; 820Sstevel@tonic-gate 830Sstevel@tonic-gate // The time when the cache should be refreshed. 840Sstevel@tonic-gate 850Sstevel@tonic-gate private long timeStamp = -1; 860Sstevel@tonic-gate 870Sstevel@tonic-gate /** 880Sstevel@tonic-gate * Construct a DATable. We get a cached table of accessable 890Sstevel@tonic-gate * DAs and scopes and the SA scope names to use for querying. 900Sstevel@tonic-gate */ 910Sstevel@tonic-gate SunDATable()920Sstevel@tonic-gate SunDATable() throws ServiceLocationException { 930Sstevel@tonic-gate 940Sstevel@tonic-gate // Remove the common scopes from the SA scopes. This will leave 950Sstevel@tonic-gate // the private, SA only scopes. 960Sstevel@tonic-gate 970Sstevel@tonic-gate saOnlyScopes = conf.getSAOnlyScopes(); 980Sstevel@tonic-gate 990Sstevel@tonic-gate Assert.slpassert(saOnlyScopes.size() > 0, 1000Sstevel@tonic-gate "no_sa_scopes", 1010Sstevel@tonic-gate new Object[0]); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate // Initialize the cache. We want the scopes that can be dynamically 1040Sstevel@tonic-gate // discovered. If we have been configured with scopes, then 1050Sstevel@tonic-gate // it will be those. If not, then we want whatever we can discovery. 1060Sstevel@tonic-gate // we only want the default version for client side. 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate cache = getWireTable(conf.getConfiguredScopes(), Defaults.version); 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /** 1130Sstevel@tonic-gate * Return a hashtable of DA equivalence classes and multicast 1140Sstevel@tonic-gate * scopes. Multicast scopes are stored in the special hashtable 1150Sstevel@tonic-gate * key MULTICAST_KEY. Unicast DA equivalence classes are stored 1160Sstevel@tonic-gate * under the key UNICAST_KEY. 1170Sstevel@tonic-gate * 1180Sstevel@tonic-gate * @param scopes Scope list for DAs needed. 1190Sstevel@tonic-gate * @return Hashtable with DA addresses as keys and scopes to contact 1200Sstevel@tonic-gate * them with as values. Any scopes not associated with a 1210Sstevel@tonic-gate * DA come back stored under the key MULTICAST_KEY. 1220Sstevel@tonic-gate * Unicast DA equivalence classes are stored 1230Sstevel@tonic-gate * under the key UNICAST_KEY. 1240Sstevel@tonic-gate */ 1250Sstevel@tonic-gate findDAScopes(Vector scopes)1260Sstevel@tonic-gate synchronized Hashtable findDAScopes(Vector scopes) 1270Sstevel@tonic-gate throws ServiceLocationException { 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate Hashtable ret = new Hashtable(); 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate Vector equivClasses = null; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate // Refresh the local cache if necessary. 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate if (timeStamp <= System.currentTimeMillis()) { 1360Sstevel@tonic-gate Vector useScopes = conf.getConfiguredScopes(); 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate cache = getWireTable(useScopes, Defaults.version); 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate equivClasses = (Vector)cache.clone(); 1430Sstevel@tonic-gate int i; 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate // Sort through the local cache, matching against the input parameter. 1460Sstevel@tonic-gate // Collect multicast scopes. 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate Vector multicastScopes = (Vector)scopes.clone(); 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate for (i = 0; i < equivClasses.size(); i++) { 1510Sstevel@tonic-gate DARecord rec = (DARecord)equivClasses.elementAt(i); 1520Sstevel@tonic-gate Vector daScopes = (Vector)rec.scopes.clone(); 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate // Filter multicast scopes first. Remove any from the multicast 1550Sstevel@tonic-gate // scope list that are in daScopes. 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate filterScopes(multicastScopes, daScopes, true); 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate // Now filter daScopes. Remove any from the daScopes that are 1600Sstevel@tonic-gate // not in the input scopes. 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate filterScopes(daScopes, scopes, false); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate // Remove this record if there are none left. 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate if (daScopes.size() <= 0) { 1670Sstevel@tonic-gate /* 1680Sstevel@tonic-gate * Must decrement the index 'i' otherwise the next iteration 1690Sstevel@tonic-gate * around the loop will miss the element immediately after 1700Sstevel@tonic-gate * the element removed. 1710Sstevel@tonic-gate * 1720Sstevel@tonic-gate * WARNING: Do not use 'i' again until the loop has 1730Sstevel@tonic-gate * iterated as it may, after decrementing, 1740Sstevel@tonic-gate * be negative. 1750Sstevel@tonic-gate */ 1760Sstevel@tonic-gate equivClasses.removeElementAt(i); 1770Sstevel@tonic-gate i--; 1780Sstevel@tonic-gate continue; 1790Sstevel@tonic-gate } 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate // Install the unicast and multicast scopes if any. 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (multicastScopes.size() > 0) { 1850Sstevel@tonic-gate ret.put(MULTICAST_KEY, multicastScopes); 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate } 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate if (equivClasses.size() > 0) { 1900Sstevel@tonic-gate ret.put(UNICAST_KEY, equivClasses); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate } 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate return ret; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate } 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /** 1990Sstevel@tonic-gate * Remove a DA by address. We only remove it from the wire table 2000Sstevel@tonic-gate * so if it's down temporarily, we'll get it back again. 2010Sstevel@tonic-gate * 2020Sstevel@tonic-gate * @param address The host address of the DA. 2030Sstevel@tonic-gate * @param scopes The scopes. 2040Sstevel@tonic-gate * @return True if removed, false if not. 2050Sstevel@tonic-gate */ 2060Sstevel@tonic-gate removeDA(InetAddress address, Vector scopes)2070Sstevel@tonic-gate boolean removeDA(InetAddress address, Vector scopes) { 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate // Sort through the table of equivalence classes in cache. 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate boolean foundit = false; 2120Sstevel@tonic-gate int i; 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate for (i = 0; i < cache.size(); i++) { 2150Sstevel@tonic-gate DARecord rec = (DARecord)cache.elementAt(i); 2160Sstevel@tonic-gate Vector daAddresses = rec.daAddresses; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate // Ignore scopes, delete if there. Scopes will always be the 2190Sstevel@tonic-gate // ones for which this DA is to be removed. 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate int j, m = daAddresses.size(); 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate for (j = 0; j < m; j++) { 2240Sstevel@tonic-gate InetAddress daaddr = (InetAddress)daAddresses.elementAt(j); 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate // If they are equal, remove it, exit loop. 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate if (address.equals(daaddr)) { 2290Sstevel@tonic-gate foundit = true; 2300Sstevel@tonic-gate daAddresses.removeElementAt(j); 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate // If the cache entry is empty, remove it. 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate if (daAddresses.size() <= 0) { 2350Sstevel@tonic-gate cache.removeElementAt(i); 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate break; 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate } 2420Sstevel@tonic-gate } 2430Sstevel@tonic-gate } 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate return foundit; 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate } 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate // Return a vector of DARecord equivalence classes by going out to the 2500Sstevel@tonic-gate // wire for them. Merge any that are in the current process' 2510Sstevel@tonic-gate // DAAddresses property. 2520Sstevel@tonic-gate getWireTable(Vector scopes, int version)2530Sstevel@tonic-gate private Vector getWireTable(Vector scopes, int version) 2540Sstevel@tonic-gate throws ServiceLocationException { 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate Vector ret = new Vector(); 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate // Get replies from the SA server. These will be CSrvMsg replies. 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate CSrvMsg msg = getSrvReply(scopes, version); 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate // Process reply into the vector of equivalence classes by adding to 2630Sstevel@tonic-gate // to those from the preconfigured DAs. 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate processReply(msg, ret); 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate // Return vector. 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate return ret; 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate getSrvReply(Vector scopes, int version)2720Sstevel@tonic-gate private CSrvMsg getSrvReply(Vector scopes, int version) 2730Sstevel@tonic-gate throws ServiceLocationException { 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate // Form the query. 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 2780Sstevel@tonic-gate int i, n = scopes.size(); 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate for (i = 0; i < n; i++) { 2810Sstevel@tonic-gate buf.append("("); 2820Sstevel@tonic-gate buf.append(SCOPES_ID); 2830Sstevel@tonic-gate buf.append("="); 2840Sstevel@tonic-gate buf.append((String)scopes.elementAt(i)); 2850Sstevel@tonic-gate buf.append(")"); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate // Add logical disjunction if there is more than one scope. 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate if (i > 1) { 2910Sstevel@tonic-gate buf.insert(0, "(|"); 2920Sstevel@tonic-gate buf.append(")"); 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate // Add version number restriction. 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate if (i > 0) { 2990Sstevel@tonic-gate buf.insert(0, "(&"); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate buf.append("("); 3040Sstevel@tonic-gate buf.append(VERSION_ID); 3050Sstevel@tonic-gate buf.append("="); 3060Sstevel@tonic-gate buf.append((new Integer(version)).toString()); 3070Sstevel@tonic-gate buf.append(")"); 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate // Add closing paren if there were any scopes. 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate if (i > 0) { 3120Sstevel@tonic-gate buf.append(")"); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate // Create the message object. Note that if scope vector is 3170Sstevel@tonic-gate // empty, the query is the null string, and so all DAs 3180Sstevel@tonic-gate // will be returned. 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate CSrvMsg msg = new CSrvMsg(Defaults.locale, 3210Sstevel@tonic-gate Defaults.SUN_DA_SERVICE_TYPE, 3220Sstevel@tonic-gate saOnlyScopes, 3230Sstevel@tonic-gate buf.toString()); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate // Send it down the pipe to the IPC process. It's a bad bug 3260Sstevel@tonic-gate // if the reply comes back as not a CSrvMsg. 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate SrvLocMsg rply = 3290Sstevel@tonic-gate Transact.transactTCPMsg(conf.getLoopback(), msg, true); 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate // Check error code. 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate if (rply == null || 3340Sstevel@tonic-gate rply.getErrorCode() != ServiceLocationException.OK) { 3350Sstevel@tonic-gate short errCode = 3360Sstevel@tonic-gate (rply == null ? 3370Sstevel@tonic-gate ServiceLocationException.INTERNAL_SYSTEM_ERROR : 3380Sstevel@tonic-gate rply.getErrorCode()); 3390Sstevel@tonic-gate throw 3400Sstevel@tonic-gate new ServiceLocationException(errCode, 3410Sstevel@tonic-gate "loopback_error", 3420Sstevel@tonic-gate new Object[] { 3430Sstevel@tonic-gate new Short(errCode)}); 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate return (CSrvMsg)rply; 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate // Process CSrvMsg reply into DA equivalence class vector 3510Sstevel@tonic-gate processReply(CSrvMsg msg, Vector ret)3520Sstevel@tonic-gate private void processReply(CSrvMsg msg, Vector ret) 3530Sstevel@tonic-gate throws ServiceLocationException { 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate int shortTimer = Integer.MAX_VALUE; 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate // Get the URLs. 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate Vector serviceURLs = msg.serviceURLs; 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate // Process each service URL. 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate int i, n = serviceURLs.size(); 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate for (i = 0; i < n; i++) { 3660Sstevel@tonic-gate ServiceURL url = (ServiceURL)serviceURLs.elementAt(i); 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate // If the time to live is less than the current minimum, 3690Sstevel@tonic-gate // save it. 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate int lifetime = url.getLifetime(); 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate if (lifetime < shortTimer) { 3740Sstevel@tonic-gate shortTimer = lifetime; 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate // Get the host name and URL part. 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate String daaddr = url.getHost(); 3810Sstevel@tonic-gate String urlpart = url.getURLPath(); 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate // Parse URL part into scope list. Be 3840Sstevel@tonic-gate // sure not to include the initial `/' in the parse. 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate StringTokenizer tk = 3870Sstevel@tonic-gate new StringTokenizer(urlpart.substring(1, 3880Sstevel@tonic-gate urlpart.length()), 3890Sstevel@tonic-gate ";"); 3900Sstevel@tonic-gate Vector daScopes = null; 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate while (tk.hasMoreElements()) { 3930Sstevel@tonic-gate String attrExp = tk.nextToken(); 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate // Convert to an SLP attribute. 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate ServiceLocationAttribute attr = 3980Sstevel@tonic-gate new ServiceLocationAttribute("(" + attrExp + ")", false); 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate // Depending on the attribute id, do something. 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate String id = attr.getId(); 4030Sstevel@tonic-gate Vector vals = attr.getValues(); 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate if (id.equals(SCOPES_ID)) { 4060Sstevel@tonic-gate daScopes = vals; 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate } else { 4090Sstevel@tonic-gate throw 4100Sstevel@tonic-gate new ServiceLocationException( 4110Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 4120Sstevel@tonic-gate "loopback_parse_error", 4130Sstevel@tonic-gate new Object[] {url}); 4140Sstevel@tonic-gate } 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate // Add it to the equivalence class. 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate addToEquivClass(daaddr, daScopes, ret); 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate } 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate // Reset the timestamp. 4240Sstevel@tonic-gate 4250Sstevel@tonic-gate timeStamp = System.currentTimeMillis() + (long)(shortTimer * 1000); 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate } 430