/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ // SLPConfig.java // /** * This class is a singleton - it has the configuration which * is the default. It reads from a configuration file and * this overrides the default. If the config file does not * expressly forbid it, the ServiceLocationManager interface * allows some of these configuration options to be modified. * This configuration is refered to by many points of the * implementation. Note that the class itself is abstract, * and is extended by two classes, one that allows slpd to * run as an SA server only, the other allows it to run * as a DA as well. * * @see com.sun.slp.ServiceLocationManager */ package com.sun.slp; import java.net.*; import java.util.*; import java.text.*; import java.io.*; /* * This class contains all configuration information. It * is hard coded to know the defaults, and will read a config * file if it is present. The config file will override the * default values and policy. If the config file allows it * the user may reset some of these values using the * ServiceLocationManager interface. * */ class SLPConfig { /** * A Java properties file defines `\' as an escape character, which * conflicts with the SLP API escape convention. Therefore, we need * to subclass properties so we can parse in the file ourselves. */ public static class SLPProperties extends Properties { SLPProperties(Properties p) { super(p); } // Parse the SLP properties file ourselves. Groan! We don't recognize // backslash as an escape. public synchronized void load(InputStream in) throws IOException { BufferedReader rd = new BufferedReader(new InputStreamReader(in)); while (rd.ready()) { String ln = rd.readLine(); // Throw out anything that begins with '#' or ';'. if (ln.startsWith("#") || ln.startsWith(";") || ln.length() <= 0) { continue; } // Parse out equals sign, if any. Note that we trim any // white space preceding or following data strings. // Although the grammar doesn't allow it, users may // enter blanks in their configuration files. // NOTE: White space is not allowed in the data of // property tag or values. If included, according // to RFC 2614, Section 2.1 these MUST be escaped, // ie. space would be represented with '\20'. // Therefore, it is *completely* safe to perform // these trim()s. They will catch errors resulting // from sloppy data entry in slp.conf files and // never corrupt or alter correctly formatted // properties. SLPTokenizer tk = new SLPTokenizer(ln, "="); if (!tk.hasMoreTokens()) {// empty line... continue; } String prop = tk.nextToken().trim(); if (prop.trim().length() <= 0) {// line has just spaces... continue; } if (!tk.hasMoreTokens()) {// line has no definition... continue; } // Register the property. String def = tk.nextToken().trim(); this.setProperty(prop, def); } } } protected SLPConfig() { // Create a temporary, default log to report any errors during // configuration. log = new StderrLog(); // Initialize properties. Properties on command line override config // file properties, and both override defaults. Properties sysProps = (Properties)(System.getProperties().clone()); // Load Defalts. try { Class.forName("com.sun.slp.Defaults"); } catch (ClassNotFoundException ex) { Assert.printMessageAndDie(this, "no_class", new Object[] {"com.sun.slp.Defaults"}); } // System properties now contain Defaults Properties defaultProps = System.getProperties(); // Load config file. SLPProperties slpProps = new SLPProperties(new Properties()); try { InputStream fis = getConfigURLStream(); if (fis != null) { slpProps.load(fis); System.setProperties(slpProps); } } catch (IOException ex) { writeLog("unparsable_config_file", new Object[] {ex.getMessage()}); } // Add config properties to Defaults, overwritting any pre-existing // entries defaultProps.putAll(slpProps); // Now add in system props, overwritting any pre-existing entries defaultProps.putAll(sysProps); System.setProperties(defaultProps); // Initialize useScopes property. This is read-only after the file // has been loaded. configuredScopes = initializeScopes("net.slp.useScopes"); saConfiguredScopes = (Vector)configuredScopes.clone(); // Add default scope to scopes for SA. if (saConfiguredScopes.size() <= 0) { saConfiguredScopes.addElement(Defaults.DEFAULT_SCOPE); } // Initialize SA scopes. This uses a Sun specific property for // scopes only used by the SA and adds in the DA scopes. saOnlyScopes = initializeScopes(DATable.SA_ONLY_SCOPES_PROP); // Initialized preconfigured DAs. preconfiguredDAs = initializePreconfiguredDAs(); // Initialize broadcast flag. broadcastOnly = Boolean.getBoolean("net.slp.isBroadcastOnly"); // Initialize logging. Default is stderr, first check for alternate. String failed = null; try { String loggerClassName = System.getProperty("sun.net.slp.loggerClass"); if (loggerClassName != null) { Class loggerClass = Class.forName(loggerClassName); // Protect against disastrous pilot error, such as trying // to use com.sun.slp.SLPConfig as the log class // (causes stack recursion) if (Class.forName("java.io.Writer").isAssignableFrom( loggerClass)) { Object logger = loggerClass.newInstance(); log = (Writer) logger; } else { failed = formatMessage( "bad_log_class", new Object[] { loggerClass.toString()}) + "\n"; } } } catch (Throwable ex) { log = null; failed = formatMessage( "bad_log", new Object[] { ex.toString()}) + "\n"; } // If no alternate log, revert to minimal default if (log == null) { log = new StderrLog(); // If the alternate log failed, log it through the default log if (failed != null) { try { synchronized (log) { log.write(failed); log.flush(); } } catch (IOException giveUp) {} } } } private InputStream getConfigURLStream() { // Open a URL onto the configuration file. String conf = System.getProperty("sun.net.slp.configURL"); if (conf == null) { conf = Defaults.SOLARIS_CONF; } InputStream str = null; try { URL confURL = new URL(conf); str = confURL.openStream(); } catch (MalformedURLException ex) { writeLog("url_malformed", new Object[] {conf}); } catch (IOException ex) { if (conf != Defaults.SOLARIS_CONF) { // don't complain if we can't find our own default writeLog("unparsable_config_file", new Object[] {ex.getMessage()}); } } return str; } // ------------------------------------------------------------ // Property manipulation functions // private boolean OKBound(int i, int lb, int ub) { if (i < lb || i > ub) return false; else return true; } int getIntProperty(String prop, int df, int lb, int ub) { int i = Integer.getInteger(prop, df).intValue(); if (OKBound(i, lb, ub)) { return i; } else { writeLog("bad_prop_tag", new Object[] {prop}); return df; } } // ------------------------------------------------------------ // Multicast radius // private int iMinMCRadius = 1; // link local scope private int iMaxMCRadius = 255; // universal scope int getMCRadius() { return getIntProperty("net.slp.multicastTTL", Defaults.iMulticastRadius, iMinMCRadius, iMaxMCRadius); } // ------------------------------------------------------------ // Heartbeat interval, seconds. // private final int iMinHeart = 2000; // 10 minutes private final int iMaxHeart = 259200000; // 3 days int getAdvertHeartbeatTime() { return getIntProperty("net.slp.DAHeartBeat", Defaults.iHeartbeat, iMinHeart, iMaxHeart); } // ------------------------------------------------------------ // Active discovery interval, seconds. // private final int iMinDisc = 300; // 5 minutes private final int iMaxDisc = 10800; // 3 hours int getActiveDiscoveryInterval() { // We allow zero in order to turn active discovery off, but // if 5 minutes is the smallest actual time. int prop = getIntProperty("net.slp.DAActiveDiscoveryInterval", Defaults.iActiveDiscoveryInterval, 0, iMaxDisc); if (prop > 0 && prop < iMinDisc) { writeLog("bad_prop_tag", new Object[] {"net.slp.DAActiveDiscoveryInterval"}); return iMinDisc; } return prop; } // ------------------------------------------------------------ // Active discovery granularity, seconds. // private int iMaxDiscGran = iMaxDisc * 2; int getActiveDiscoveryGranularity() { return getIntProperty("sun.net.slp.DAActiveDiscoveryGranularity", Defaults.iActiveDiscoveryGranularity, 0, iMaxDiscGran); } // ------------------------------------------------------------ // Bound for random wait, milliseconds. // private final int iMinWait = 1000; // 1 sec. private final int iMaxWait = 3000; // 3 sec. int getRandomWaitBound() { return getIntProperty("net.slp.randomWaitBound", Defaults.iRandomWaitBound, iMinWait, iMaxWait); } private static Random randomWait = null; long getRandomWait() { if (randomWait == null) { randomWait = new Random(); } double r = randomWait.nextDouble(); double max = (double)getRandomWaitBound(); return (long)(max * r); } // ------------------------------------------------------------ // TCP timeout, milliseconds. // final static private int iMinTimeout = 100; final static private int iMaxTimeout = 360000; int getTCPTimeout() { return getIntProperty("sun.net.slp.TCPTimeout", Defaults.iTCPTimeout, iMinTimeout, iMaxTimeout); } // ------------------------------------------------------------ // Path MTU // private final int iMinMTU = 128; // used for some ppp connections private final int iMaxMTU = 8192; // used on some LANs int getMTU() { return getIntProperty("net.slp.MTU", Defaults.iMTU, iMinMTU, iMaxMTU); } // ------------------------------------------------------------ // Serialized registrations. // String getSerializedRegURL() { return System.getProperty("net.slp.serializedRegURL", null); } // ------------------------------------------------------------ // Are we running as a DA or SA server? // protected static boolean isSA = false; boolean isDA() { return false; } boolean isSA() { return isSA; } // ------------------------------------------------------------ // DA and SA attributes // Vector getDAAttributes() { return getAttributes("net.slp.DAAttributes", Defaults.defaultDAAttributes, true); } Vector getSAAttributes() { return getAttributes("net.slp.SAAttributes", Defaults.defaultSAAttributes, false); } private Vector getAttributes(String prop, Vector defaults, boolean daAttrs) { String attrList = System.getProperty(prop); if (attrList == null || attrList.length() <= 0) { return (Vector)defaults.clone(); } try { Vector sAttrs = SrvLocHeader.parseCommaSeparatedListIn(attrList, false); Vector attrs = new Vector(); int i, n = sAttrs.size(); // Create attribute objects. for (i = 0; i < n; i++) { String attrExp = (String)sAttrs.elementAt(i); ServiceLocationAttribute attr = new ServiceLocationAttribute(attrExp, false); // If this is the min-refresh-interval, then check the value. if (daAttrs && attr.getId().equals( Defaults.MIN_REFRESH_INTERVAL_ATTR_ID)) { Vector values = attr.getValues(); boolean errorp = true; if (values != null && values.size() == 1) { Object val = values.elementAt(0); if (val instanceof Integer) { int ival = ((Integer)val).intValue(); if (ival >= 0 && ival <= ServiceURL.LIFETIME_MAXIMUM) { errorp = false; } } } // Throw exception if it didn't work. if (errorp) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "syntax_error_prop", new Object[] {prop, attrs}); } } // Add attribute to vector. attrs.addElement(attr); } return attrs; } catch (Exception ex) { writeLog("syntax_error_prop", new Object[] {prop, attrList}); return (Vector)defaults.clone(); } } // ------------------------------------------------------------- // Do we support V1? // boolean isV1Supported() { return false; } // ------------------------------------------------------------- // Queue length for server socket. // int getServerSocketQueueLength() { return getIntProperty("sun.net.slp.serverSocketQueueLength", Defaults.iSocketQueueLength, 0, Integer.MAX_VALUE); } // ------------------------------------------------------------ // Testing options // boolean traceAll() {// not official! return Boolean.getBoolean("sun.net.slp.traceALL"); } boolean regTest() { if (Boolean.getBoolean("sun.net.slp.traceALL") || Boolean.getBoolean("net.slp.traceReg")) return true; else return false; } boolean traceMsg() { if (Boolean.getBoolean("sun.net.slp.traceALL") || Boolean.getBoolean("net.slp.traceMsg")) return true; else return false; } boolean traceDrop() { if (Boolean.getBoolean("sun.net.slp.traceALL") || Boolean.getBoolean("net.slp.traceDrop")) return true; else return false; } boolean traceDATraffic() { if (Boolean.getBoolean("sun.net.slp.traceALL") || Boolean.getBoolean("net.slp.traceDATraffic")) return true; else return false; } // cannot use Boolean.getBoolean as the default is 'true' // using that mechanism, absense would be considered 'false' boolean passiveDADetection() { String sPassive = System.getProperty("net.slp.passiveDADetection", "true"); if (sPassive.equalsIgnoreCase("true")) return true; else return false; } // Initialized when the SLPConfig object is created to avoid changing // during the program. private boolean broadcastOnly = false; boolean isBroadcastOnly() { return broadcastOnly; } // ------------------------------------------------------------ // Multicast/broadcast socket mangement. // DatagramSocket broadSocket = null; // cached broadcast socket. // Reopen the multicast/broadcast socket bound to the // interface. If groups is not null, then join all // the groups. Otherwise, this is send only. DatagramSocket refreshMulticastSocketOnInterface(InetAddress interfac, Vector groups) { try { // Reopen it. DatagramSocket dss = getMulticastSocketOnInterface(interfac, (groups == null ? true:false)); if ((groups != null) && (dss instanceof MulticastSocket)) { int i, n = groups.size(); MulticastSocket mss = (MulticastSocket)dss; for (i = 0; i < n; i++) { InetAddress maddr = (InetAddress)groups.elementAt(i); mss.joinGroup(maddr); } } return dss; } catch (Exception ex) { // Any exception in error recovery causes program to die. Assert.slpassert(false, "cast_socket_failure", new Object[] {ex, ex.getMessage()}); } return null; } // Open a multicast/broadcast socket on the interface. Note that if // the socket is broadcast, the network interface is not specified in the // creation message. Is it bound to all interfaces? The isSend parameter // specifies whether the socket is for send only. DatagramSocket getMulticastSocketOnInterface(InetAddress interfac, boolean isSend) throws ServiceLocationException { DatagramSocket castSocket = null; // Substitute broadcast if we are configured for it. if (isBroadcastOnly()) { try { // If transmit, then simply return a new socket. if (isSend) { castSocket = new DatagramSocket(); } else { // Return cached socket if there. if (broadSocket != null) { castSocket = broadSocket; } else { // Make a new broadcast socket. castSocket = new DatagramSocket(Defaults.iSLPPort, getBroadcastAddress()); } // Cache for future reference. broadSocket = castSocket; } } catch (SocketException ex) { throw new ServiceLocationException( ServiceLocationException.NETWORK_INIT_FAILED, "socket_creation_failure", new Object[] { getBroadcastAddress(), ex.getMessage()}); } } else { // Create a multicast socket. MulticastSocket ms; try { if (isSend) { ms = new MulticastSocket(); } else { ms = new MulticastSocket(Defaults.iSLPPort); } } catch (IOException ex) { throw new ServiceLocationException( ServiceLocationException.NETWORK_INIT_FAILED, "socket_creation_failure", new Object[] {interfac, ex.getMessage()}); } try { // Set the TTL and the interface on the multicast socket. // Client is responsible for joining group. ms.setTimeToLive(getMCRadius()); ms.setInterface(interfac); } catch (IOException ex) { throw new ServiceLocationException( ServiceLocationException.NETWORK_INIT_FAILED, "socket_initializtion_failure", new Object[] {interfac, ex.getMessage()}); } castSocket = ms; } return castSocket; } // ------------------------------------------------------------ // Type hint // // Return a vector of ServiceType objects for the type hint. Vector getTypeHint() { Vector hint = new Vector(); String sTypeList = System.getProperty("net.slp.typeHint", ""); if (sTypeList.length() <= 0) { return hint; } // Create a vector of ServiceType objects for the type hint. try { hint = SrvLocHeader.parseCommaSeparatedListIn(sTypeList, true); int i, n = hint.size(); for (i = 0; i < n; i++) { String type = (String)hint.elementAt(i); hint.setElementAt(new ServiceType(type), i); } } catch (ServiceLocationException ex) { writeLog("syntax_error_prop", new Object[] {"net.slp.typeHint", sTypeList}); hint.removeAllElements(); } return hint; } // ------------------------------------------------------------ // Configured scope handling // // Vector of configured scopes. private Vector configuredScopes = null; // Vector of configures scopes for SA. private Vector saConfiguredScopes = null; // Vector of scopes only in the sa server. private Vector saOnlyScopes = null; // Return the configured scopes. Vector getConfiguredScopes() { return (Vector)configuredScopes.clone(); } // Return SA scopes. Vector getSAOnlyScopes() { return (Vector)saOnlyScopes.clone(); } // Return the configured scopes for the SA. Vector getSAConfiguredScopes() { return (Vector)saConfiguredScopes.clone(); } // Add scopes discovered during preconfigured DA contact. // These count as configured scopes. void addPreconfiguredDAScopes(Vector scopes) { int i, n = scopes.size(); for (i = 0; i < n; i++) { Object scope = scopes.elementAt(i); if (!configuredScopes.contains(scope)) { configuredScopes.addElement(scope); } // There better be none extra here for the SA server/DA. if (isSA() || isDA()) { Assert.slpassert(saConfiguredScopes.contains(scope), "sa_new_scope", new Object[] {scope, saConfiguredScopes}); } } } // Initialize the scopes list on property. private Vector initializeScopes(String prop) { String sScopes = System.getProperty(prop); if (sScopes == null || sScopes.length() <= 0) { return new Vector(); } try { Vector vv = SrvLocHeader.parseCommaSeparatedListIn(sScopes, true); // Unescape scope strings. SLPHeaderV2.unescapeScopeStrings(vv); // Validate, lower case scope names. DATable.validateScopes(vv, getLocale()); if (vv.size() > 0) { return vv; } } catch (ServiceLocationException ex) { writeLog("syntax_error_prop", new Object[] { prop, sScopes}); } return new Vector(); } // Vector of preconfigured DAs. Read only after initialized. private Vector preconfiguredDAs = null; // Return a vector of DA addresses. Vector getPreconfiguredDAs() { return (Vector)preconfiguredDAs.clone(); } // Initialize preconfigured DA list. private Vector initializePreconfiguredDAs() { String sDAList = System.getProperty("net.slp.DAAddresses", ""); Vector ret = new Vector(); sDAList.trim(); if (sDAList.length() <= 0) { return ret; } try { ret = SrvLocHeader.parseCommaSeparatedListIn(sDAList, true); } catch (ServiceLocationException ex) { writeLog("syntax_error_prop", new Object[] {"net.slp.DAAddress", sDAList}); return ret; } // Convert to InetAddress objects. int i; for (i = 0; i < ret.size(); i++) { String da = ""; try { da = ((String)ret.elementAt(i)).trim(); InetAddress daAddr = InetAddress.getByName(da); ret.setElementAt(daAddr, i); } catch (UnknownHostException ex) { writeLog("resolve_failed", new Object[] {da}); /* * Must decrement the index 'i' otherwise the next iteration * around the loop will miss the element immediately after * the element removed. * * WARNING: Do not use 'i' again until the loop has * iterated as it may, after decrementing, * be negative. */ ret.removeElementAt(i); i--; continue; } } return ret; } // ------------------------------------------------------------ // SLPv1 Support Switches // boolean getSLPv1NotSupported() {// not official! return Boolean.getBoolean("sun.net.slp.SLPv1NotSupported"); } boolean getAcceptSLPv1UnscopedRegs() {// not official! if (!getSLPv1NotSupported()) { return Boolean.getBoolean("sun.net.slp.acceptSLPv1UnscopedRegs"); } return false; } // ------------------------------------------------------------ // Accessor for SLPConfig object // protected static SLPConfig theSLPConfig = null; static SLPConfig getSLPConfig() { if (theSLPConfig == null) { theSLPConfig = new SLPConfig(); } return theSLPConfig; } /** * @return Maximum number of messages/objects to return. */ int getMaximumResults() { int i = Integer.getInteger("net.slp.maxResults", Defaults.iMaximumResults).intValue(); if (i == -1) { i = Integer.MAX_VALUE; } if (OKBound(i, 1, Integer.MAX_VALUE)) { return i; } else { writeLog("bad_prop_tag", new Object[] { "net.slp.maxResults"}); return Defaults.iMaximumResults; } } /** * Convert a language tag into a locale. */ static Locale langTagToLocale(String ltag) { // We treat the first part as the ISO 639 language and the // second part as the ISO 3166 country tag, even though RFC // 1766 doesn't necessarily require that. We should probably // use a lookup table here to determine if they are correct. StringTokenizer tk = new StringTokenizer(ltag, "-"); String lang = ""; String country = ""; if (tk.hasMoreTokens()) { lang = tk.nextToken(); if (tk.hasMoreTokens()) { country = tk.nextToken(""); // country name may have "-" in it... } } return new Locale(lang, country); } /** * Convert a Locale object into a language tag for output. * * @param locale The Locale. * @return String with the language tag encoded. */ static String localeToLangTag(Locale locale) { // Construct the language tag. String ltag = locale.getCountry(); ltag = locale.getLanguage() + (ltag.length() <= 0 ? "" : ("-" + ltag)); return ltag; } /** * @return the language requests will be made in. */ static Locale getLocale() { String s = System.getProperty("net.slp.locale"); if (s != null && s.length() > 0) { return langTagToLocale(s); } else { // Return the Java default if the SLP property is not set. return Locale.getDefault(); } } /** * @return the InetAddress of the broadcast interface. */ static private InetAddress broadcastAddress; static InetAddress getBroadcastAddress() { if (broadcastAddress == null) { try { broadcastAddress = InetAddress.getByName(Defaults.sBroadcast); } catch (UnknownHostException uhe) { Assert.slpassert(false, "cast_address_failure", new Object[] {Defaults.sBroadcast}); } } return broadcastAddress; } /** * @return the InetAddress of the multicast group. */ static private InetAddress multicastAddress; static InetAddress getMulticastAddress() { if (multicastAddress == null) { try { multicastAddress = InetAddress.getByName(Defaults.sGeneralSLPMCAddress); } catch (UnknownHostException uhe) { Assert.slpassert(false, "cast_address_failure", new Object[] {Defaults.sGeneralSLPMCAddress}); } } return multicastAddress; } /** * @return the interfaces on which SLP should listen and transmit. */ private static Vector interfaces = null; Vector getInterfaces() { if (interfaces == null) { InetAddress iaLocal = null; // Get local host. try { iaLocal = InetAddress.getLocalHost(); } catch (UnknownHostException ex) { Assert.slpassert(false, "resolve_failed", new Object[] {"localhost"}); } String mcastI = System.getProperty("net.slp.interfaces"); interfaces = new Vector(); // Only add local host if nothing else is given. if (mcastI == null || mcastI.length() <= 0) { interfaces.addElement(iaLocal); return interfaces; } Vector nintr; try { nintr = SrvLocHeader.parseCommaSeparatedListIn(mcastI, true); } catch (ServiceLocationException ex) { writeLog("syntax_error_prop", new Object[] { "net.slp.multicastInterfaces", mcastI}); // Add local host. interfaces.addElement(iaLocal); return interfaces; } // See if they are really there. int i, n = nintr.size(); for (i = 0; i < n; i++) { InetAddress ia; String host = (String)nintr.elementAt(i); try { ia = InetAddress.getByName(host); } catch (UnknownHostException ex) { writeLog("unknown_interface", new Object[] {host, "net.slp.multicastInterfaces"}); continue; } if (!interfaces.contains(ia)) { // Add default at beginning. if (ia.equals(iaLocal)) { interfaces.insertElementAt(ia, 0); } else { interfaces.addElement(ia); } } } } return interfaces; } /** * @return An InetAddress object representing 127.0.0.1 */ InetAddress getLoopback() { InetAddress iaLoopback = null; try { iaLoopback = InetAddress.getByName(Defaults.LOOPBACK_ADDRESS); } catch (UnknownHostException ex) { Assert.slpassert(false, "resolve_failed", new Object[] {"localhost loopback"}); } return iaLoopback; } /** * @return The default interface, which should be the first in the * interfaces vector Vector. */ InetAddress getLocalHost() { Vector inter = getInterfaces(); return (InetAddress)inter.elementAt(0); } // Return true if the address is one of the local interfaces. boolean isLocalHostSource(InetAddress addr) { // First check loopback if (addr.equals(getLoopback())) { return true; } return interfaces.contains(addr); } // ----------------- // Timeouts // // Return the maximum wait for multicast convergence. final static private int iMultiMin = 1000; // one second final static private int iMultiMax = 60000; // one minute int getMulticastMaximumWait() { return getIntProperty("net.slp.multicastMaximumWait", Defaults.iMulticastMaxWait, iMultiMin, iMultiMax); } /* * @return Vector of timeouts for multicast convergence. */ int[] getMulticastTimeouts() { int[] timeouts = parseTimeouts("net.slp.multicastTimeouts", Defaults.a_iConvergeTimeout); timeouts = capTimeouts("net.slp.multicastTimeouts", timeouts, false, 0, 0); return timeouts; } /** * @return Vector of timeouts to try for datagram transmission. */ int[] getDatagramTimeouts() { int[] timeouts = parseTimeouts("net.slp.datagramTimeouts", Defaults.a_iDatagramTimeout); timeouts = capTimeouts("net.slp.datagramTimeouts", timeouts, true, iMultiMin, iMultiMax); return timeouts; } /** * @return Vector of timeouts for DA discovery multicast. */ int[] getDADiscoveryTimeouts() { int[] timeouts = parseTimeouts("net.slp.DADiscoveryTimeouts", Defaults.a_iDADiscoveryTimeout); timeouts = capTimeouts("net.slp.DADiscoveryTimeouts", timeouts, false, 0, 0); return timeouts; } /** * This method ensures that all the timeouts are within valid ranges. * The sum of all timeouts for the given property name must not * exceed the value returned by getMulticastMaximumWait(). If * the sum of all timeouts does exceed the maximum wait period the * timeouts are averaged out so that the sum equals the maximum wait * period. *
* Additional range checking is also performed when rangeCheck * is true. Then the sum of all timeouts must also be between min * and max. If the sum of all timeouts is not within the range * the average is taken from the closest range boundary. * * @param property * Name of timeout property being capped. This is only present for * reporting purposes and no actual manipulation of the property * is made within this method. * @param timeouts * Array of timeout values. * @param rangeCheck * Indicator of whether additional range checking is required. When * false min and max are ignored. * @param min * Additional range checking lower boundary. * @param max * Additional range checking upper boundary. * @return * Array of capped timeouts. Note this may be the same array as * passed in (timeouts). */ private int[] capTimeouts(String property, int[] timeouts, boolean rangeCheck, int min, int max) { int averagedTimeout; int totalWait = 0; for (int index = 0; index < timeouts.length; index++) { totalWait += timeouts[index]; } if (rangeCheck) { // If sum of timeouts within limits then finished. if (totalWait >= min && totalWait <= max) { return timeouts; } // Average out the timeouts so the sum is equal to the closest // range boundary. if (totalWait < min) { averagedTimeout = min / timeouts.length; } else { averagedTimeout = max / timeouts.length; } writeLog("capped_range_timeout_prop", new Object[] {property, String.valueOf(totalWait), String.valueOf(min), String.valueOf(max), String.valueOf(timeouts.length), String.valueOf(averagedTimeout)}); } else { // Sum of all timeouts must not exceed this value. int maximumWait = getMulticastMaximumWait(); // If sum of timeouts within limits then finished. if (totalWait <= maximumWait) { return timeouts; } // Average out the timeouts so the sum is equal to the maximum // timeout. averagedTimeout = maximumWait / timeouts.length; writeLog("capped_timeout_prop", new Object[] {property, String.valueOf(totalWait), String.valueOf(maximumWait), String.valueOf(timeouts.length), String.valueOf(averagedTimeout)}); } for (int index = 0; index < timeouts.length; index++) { timeouts[index] = averagedTimeout; } return timeouts; } private int[] parseTimeouts(String property, int[] defaults) { String sTimeouts = System.getProperty(property); if (sTimeouts == null || sTimeouts.length() <= 0) { return defaults; } Vector timeouts = null; try { timeouts = SrvLocHeader.parseCommaSeparatedListIn(sTimeouts, true); } catch (ServiceLocationException ex) { writeLog("syntax_error_prop", new Object[] {property, sTimeouts}); return defaults; } int iCount = 0; int[] iTOs = new int[timeouts.size()]; for (Enumeration en = timeouts.elements(); en.hasMoreElements(); ) { String s1 = (String)en.nextElement(); try { iTOs[iCount] = Integer.parseInt(s1); } catch (NumberFormatException nfe) { writeLog("syntax_error_prop", new Object[] {property, sTimeouts}); return defaults; } if (iTOs[iCount] < 0) { writeLog("invalid_timeout_prop", new Object[] {property, String.valueOf(iTOs[iCount])}); return defaults; } iCount++; } return iTOs; } // ----------------------------- // SLP Time Calculation // /** * Returns the number of seconds since 00:00 Universal Coordinated * Time, January 1, 1970. * * Java returns the number of milliseconds, so all the method does is * divide by 1000. * * This implementation still will have a problem when the Java time * values wraps, but there isn't much we can do now. */ static long currentSLPTime() { return (System.currentTimeMillis() / 1000); } /* security */ // Indicates whether security class is available. boolean getSecurityEnabled() { return securityEnabled; } private static boolean securityEnabled; // Indicates whether the securityEnabled property is true boolean getHasSecurity() { return securityEnabled && (new Boolean(System.getProperty("net.slp.securityEnabled", "false")).booleanValue()); } // I18N Support. private static final String BASE_BUNDLE_NAME = "com/sun/slp/ClientLib"; ResourceBundle getMessageBundle(Locale locale) { ResourceBundle msgBundle = null; // First try the Solaris Java locale area try { URL[] urls = new URL[] {new URL("file:/usr/share/lib/locale/")}; URLClassLoader ld = new URLClassLoader(urls); msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale, ld); return msgBundle; } catch (MalformedURLException e) { // shouldn't get here } catch (MissingResourceException ex) { System.err.println("Missing resource bundle ``"+ "/usr/share/lib/locale/" + BASE_BUNDLE_NAME + "'' for locale ``" + locale + "''; trying default..."); } try { msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale); } catch (MissingResourceException ex) { // can't localize this one! // We can't print out to the log, because we may be in the // process of trying to. System.err.println("Missing resource bundle ``"+ BASE_BUNDLE_NAME+ "'' for locale ``"+ locale+ "''"); // Hosed if the default locale is missing. if (locale.equals(Defaults.locale)) { System.err.println("Exiting..."); System.exit(1); } // Otherwise, return the default locale. System.err.println("Using SLP default locale ``" + Defaults.locale + "''"); msgBundle = getMessageBundle(Defaults.locale); } return msgBundle; } String formatMessage(String msgTag, Object[] params) { ResourceBundle bundle = getMessageBundle(getLocale()); return formatMessageInternal(msgTag, params, bundle); } // MessageFormat is picky about types. Convert the params into strings. static void convertToString(Object[] params) { int i, n = params.length; for (i = 0; i < n; i++) { if (params[i] != null) { params[i] = params[i].toString(); } else { params[i] = ""; } } } static String formatMessageInternal(String msgTag, Object[] params, ResourceBundle bundle) { String pattern = ""; try { pattern = bundle.getString(msgTag); } catch (MissingResourceException ex) { // Attempt to report error. Can't use Assert here because it // calls back into SLPConfig. String msg = "Can''t find message ``{0}''''."; try { pattern = bundle.getString("cant_find_resource"); msg = MessageFormat.format(pattern, new Object[] {msgTag}); } catch (MissingResourceException exx) { } System.err.println(msg); System.exit(-1); } convertToString(params); return MessageFormat.format(pattern, params); } // logging. // Protected so slpd can replace it. protected Writer log; // Synchronized so writes from multiple threads don't get interleaved. void writeLog(String msgTag, Object[] params) { // MessageFormat is picky about types. Convert the params into strings. convertToString(params); try { synchronized (log) { log.write(formatMessage(msgTag, params)); log.flush(); } } catch (IOException ex) {} } void writeLogLine(String msgTag, Object[] params) { try { String pattern = getMessageBundle(getLocale()).getString(msgTag); synchronized (log) { log.write(formatMessage(msgTag, params)); log.write("\n"); log.flush(); } } catch (IOException ex) {} } static String getDateString() { DateFormat df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, getLocale()); Calendar calendar = Calendar.getInstance(getLocale()); return df.format(calendar.getTime()); } // On load, check whether the signature class is available, and turn // security off if not. static { securityEnabled = true; try { Class c = Class.forName("com.sun.slp.AuthBlock"); } catch (ClassNotFoundException e) { securityEnabled = false; } } }