xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPConfig.java (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
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 1999-2003 Sun Microsystems, Inc.  All rights reserved.
23   * Use is subject to license terms.
24   *
25   */
26  
27  //  SLPConfig.java
28  //
29  
30  /**
31   * This class is a singleton - it has the configuration which
32   * is the default.  It reads from a configuration file and
33   * this overrides the default.  If the config file does not
34   * expressly forbid it, the ServiceLocationManager interface
35   * allows some of these configuration options to be modified.
36   * This configuration is refered to by many points of the
37   * implementation. Note that the class itself is abstract,
38   * and is extended by two classes, one that allows slpd to
39   * run as an SA server only, the other allows it to run
40   * as a DA as well.
41   *
42   * @see com.sun.slp.ServiceLocationManager
43   */
44  
45  package com.sun.slp;
46  
47  import java.net.*;
48  import java.util.*;
49  import java.text.*;
50  import java.io.*;
51  
52  /*
53   * This class contains all configuration information.  It
54   * is hard coded to know the defaults, and will read a config
55   * file if it is present.  The config file will override the
56   * default values and policy.  If the config file allows it
57   * the user may reset some of these values using the
58   * ServiceLocationManager interface.
59   *
60   */
61  class SLPConfig {
62  
63      /**
64       * A Java properties file defines `\' as an escape character, which
65       * conflicts with the SLP API escape convention. Therefore, we need
66       * to subclass properties so we can parse in the file ourselves.
67       */
68  
69      public static class SLPProperties extends Properties {
70  
SLPProperties(Properties p)71  	SLPProperties(Properties p) {
72  	    super(p);
73  
74  	}
75  
76  	// Parse the SLP properties file ourselves. Groan! We don't recognize
77  	//  backslash as an escape.
78  
load(InputStream in)79  	public synchronized void load(InputStream in) throws IOException {
80  
81  	    BufferedReader rd = new BufferedReader(new InputStreamReader(in));
82  
83  	    while (rd.ready()) {
84  		String ln = rd.readLine();
85  
86  		// Throw out anything that begins with '#' or ';'.
87  
88  		if (ln.startsWith("#") ||
89  		    ln.startsWith(";") ||
90  		    ln.length() <= 0) {
91  		    continue;
92  
93  		}
94  
95  		// Parse out equals sign, if any. Note that we trim any
96  		//  white space preceding or following data strings.
97  		//  Although the grammar doesn't allow it, users may
98  		//  enter blanks in their configuration files.
99  		// NOTE:  White space is not allowed in the data of
100  		//  property tag or values.  If included, according
101  		//  to RFC 2614, Section 2.1 these MUST be escaped,
102  		//  ie. space would be represented with '\20'.
103  		//  Therefore, it is *completely* safe to perform
104  		//  these trim()s.  They will catch errors resulting
105  		//  from sloppy data entry in slp.conf files and
106  		//  never corrupt or alter correctly formatted
107  		//  properties.
108  
109  		SLPTokenizer tk = new SLPTokenizer(ln, "=");
110  
111  		if (!tk.hasMoreTokens()) {// empty line...
112  		    continue;
113  
114  		}
115  
116  		String prop = tk.nextToken().trim();
117  
118  		if (prop.trim().length() <= 0) {// line has just spaces...
119  		    continue;
120  
121  		}
122  
123  		if (!tk.hasMoreTokens()) {// line has no definition...
124  		    continue;
125  
126  		}
127  
128  		// Register the property.
129  		String def = tk.nextToken().trim();
130  		this.setProperty(prop, def);
131  	    }
132  	}
133  
134      }
135  
SLPConfig()136      protected SLPConfig() {
137  
138  	// Create a temporary, default log to report any errors during
139  	//  configuration.
140  	log = new StderrLog();
141  
142  	// Initialize properties. Properties on command line override config
143  	//  file properties, and both override defaults.
144  
145  	Properties sysProps = (Properties)(System.getProperties().clone());
146  
147  	// Load Defalts.
148  
149  	try {
150  	    Class.forName("com.sun.slp.Defaults");
151  
152  	} catch (ClassNotFoundException ex) {
153  
154  	    Assert.printMessageAndDie(this,
155  				      "no_class",
156  				      new Object[] {"com.sun.slp.Defaults"});
157  	}
158  
159  	// System properties now contain Defaults
160  	Properties defaultProps = System.getProperties();
161  
162  	// Load config file.
163  
164  	SLPProperties slpProps = new SLPProperties(new Properties());
165  	try {
166  	    InputStream fis = getConfigURLStream();
167  	    if (fis != null) {
168  		slpProps.load(fis);
169  		System.setProperties(slpProps);
170  	    }
171  
172  	} catch (IOException ex) {
173  	    writeLog("unparsable_config_file",
174  		     new Object[] {ex.getMessage()});
175  	}
176  
177  	// Add config properties to Defaults, overwritting any pre-existing
178  	//  entries
179  	defaultProps.putAll(slpProps);
180  
181  	// Now add in system props, overwritting any pre-existing entries
182  	defaultProps.putAll(sysProps);
183  
184  	System.setProperties(defaultProps);
185  
186  
187  	// Initialize useScopes property. This is read-only after the file
188  	//  has been loaded.
189  
190  	configuredScopes = initializeScopes("net.slp.useScopes");
191  	saConfiguredScopes = (Vector)configuredScopes.clone();
192  
193  	// Add default scope to scopes for SA.
194  
195  	if (saConfiguredScopes.size() <= 0) {
196  	    saConfiguredScopes.addElement(Defaults.DEFAULT_SCOPE);
197  
198  	}
199  
200  	// Initialize SA scopes. This uses a Sun specific property for
201  	//  scopes only used by the SA and adds in the DA scopes.
202  
203  	saOnlyScopes = initializeScopes(DATable.SA_ONLY_SCOPES_PROP);
204  
205  	// Initialized preconfigured DAs.
206  
207  	preconfiguredDAs = initializePreconfiguredDAs();
208  
209  	// Initialize broadcast flag.
210  
211  	broadcastOnly = Boolean.getBoolean("net.slp.isBroadcastOnly");
212  
213  	// Initialize logging. Default is stderr, first check for alternate.
214  
215  	String failed = null;
216  
217  	try {
218  	    String loggerClassName =
219  		System.getProperty("sun.net.slp.loggerClass");
220  	    if (loggerClassName != null) {
221  		Class loggerClass = Class.forName(loggerClassName);
222  		// Protect against disastrous pilot error, such as trying
223  		// to use com.sun.slp.SLPConfig as the log class
224  		// (causes stack recursion)
225  		if (Class.forName("java.io.Writer").isAssignableFrom(
226  							loggerClass)) {
227  		    Object logger = loggerClass.newInstance();
228  		    log = (Writer) logger;
229  		} else {
230  		    failed = formatMessage(
231  					   "bad_log_class",
232  					   new Object[] {
233  			loggerClass.toString()}) + "\n";
234  		}
235  	    }
236  
237  	} catch (Throwable ex) {
238  	    log = null;
239  	    failed = formatMessage(
240  				   "bad_log",
241  				   new Object[] {
242  		ex.toString()}) + "\n";
243  	}
244  
245  	// If no alternate log, revert to minimal default
246  	if (log == null) {
247  	    log = new StderrLog();
248  
249  	    // If the alternate log failed, log it through the default log
250  	    if (failed != null) {
251  		try {
252  		    synchronized (log) {
253  			log.write(failed);
254  			log.flush();
255  		    }
256  		} catch (IOException giveUp) {}
257  	    }
258  	}
259  
260      }
261  
getConfigURLStream()262      private InputStream getConfigURLStream() {
263  
264  	// Open a URL onto the configuration file.
265  
266  	String conf = System.getProperty("sun.net.slp.configURL");
267  
268  	if (conf == null) {
269  	    conf = Defaults.SOLARIS_CONF;
270  
271  	}
272  
273  	InputStream str = null;
274  
275  	try {
276  
277  	    URL confURL = new URL(conf);
278  
279  	    str = confURL.openStream();
280  
281  	} catch (MalformedURLException ex) {
282  	    writeLog("url_malformed",
283  		     new Object[] {conf});
284  
285  	} catch (IOException ex) {
286  	    if (conf != Defaults.SOLARIS_CONF) {
287  		// don't complain if we can't find our own default
288  		writeLog("unparsable_config_file",
289  			 new Object[] {ex.getMessage()});
290  	    }
291  
292  	}
293  
294  	return str;
295      }
296  
297      // ------------------------------------------------------------
298      // Property manipulation functions
299      //
300  
OKBound(int i, int lb, int ub)301      private boolean OKBound(int i, int lb, int ub) {
302  	if (i < lb || i > ub)
303  	    return false;
304  	else
305  	    return true;
306      }
307  
getIntProperty(String prop, int df, int lb, int ub)308      int getIntProperty(String prop, int df, int lb, int ub) {
309  
310  	int i = Integer.getInteger(prop, df).intValue();
311  
312  	if (OKBound(i, lb, ub)) {
313  	    return i;
314  
315  	} else {
316  	    writeLog("bad_prop_tag", new Object[] {prop});
317  
318  	    return df;
319  	}
320      }
321  
322      // ------------------------------------------------------------
323      // Multicast radius
324      //
325      private int iMinMCRadius = 1;   // link local scope
326      private int iMaxMCRadius = 255; // universal scope
327  
getMCRadius()328      int getMCRadius() {
329  	return getIntProperty("net.slp.multicastTTL",
330  			      Defaults.iMulticastRadius,
331  			      iMinMCRadius,
332  			      iMaxMCRadius);
333      }
334  
335      // ------------------------------------------------------------
336      // Heartbeat interval, seconds.
337      //
338      private final int iMinHeart = 2000;    // 10 minutes
339      private final int iMaxHeart = 259200000; // 3 days
340  
getAdvertHeartbeatTime()341      int getAdvertHeartbeatTime() {
342  	return getIntProperty("net.slp.DAHeartBeat",
343  			      Defaults.iHeartbeat,
344  			      iMinHeart,
345  			      iMaxHeart);
346      }
347  
348      // ------------------------------------------------------------
349      // Active discovery interval, seconds.
350      //
351  
352      private final int iMinDisc = 300;    // 5 minutes
353      private final int iMaxDisc = 10800;  // 3 hours
354  
getActiveDiscoveryInterval()355      int getActiveDiscoveryInterval() {
356  
357  	// We allow zero in order to turn active discovery off, but
358  	//  if 5 minutes is the smallest actual time.
359  
360  	int prop = getIntProperty("net.slp.DAActiveDiscoveryInterval",
361  				  Defaults.iActiveDiscoveryInterval,
362  				  0,
363  				  iMaxDisc);
364  	if (prop > 0 && prop < iMinDisc) {
365  	    writeLog("bad_prop_tag",
366  		     new Object[] {"net.slp.DAActiveDiscoveryInterval"});
367  	    return iMinDisc;
368  
369  	}
370  
371  	return prop;
372      }
373  
374  
375      // ------------------------------------------------------------
376      // Active discovery granularity, seconds.
377      //
378  
379      private int iMaxDiscGran = iMaxDisc * 2;
380  
getActiveDiscoveryGranularity()381      int getActiveDiscoveryGranularity() {
382  	return getIntProperty("sun.net.slp.DAActiveDiscoveryGranularity",
383  			      Defaults.iActiveDiscoveryGranularity,
384  			      0,
385  			      iMaxDiscGran);
386      }
387  
388      // ------------------------------------------------------------
389      // Bound for random wait, milliseconds.
390      //
391  
392      private final int iMinWait = 1000;  // 1 sec.
393      private final int iMaxWait = 3000;  // 3 sec.
394  
getRandomWaitBound()395      int getRandomWaitBound() {
396  	return getIntProperty("net.slp.randomWaitBound",
397  			      Defaults.iRandomWaitBound,
398  			      iMinWait,
399  			      iMaxWait);
400      }
401  
402      private static Random randomWait = null;
403  
getRandomWait()404      long getRandomWait() {
405  
406  	if (randomWait == null) {
407  	    randomWait = new Random();
408  	}
409  
410  	double r = randomWait.nextDouble();
411  	double max = (double)getRandomWaitBound();
412  
413  	return (long)(max * r);
414      }
415  
416      // ------------------------------------------------------------
417      // TCP timeout, milliseconds.
418      //
419      final static private int iMinTimeout = 100;
420      final static private int iMaxTimeout = 360000;
421  
getTCPTimeout()422      int getTCPTimeout() {
423  	return getIntProperty("sun.net.slp.TCPTimeout",
424  			      Defaults.iTCPTimeout,
425  			      iMinTimeout,
426  			      iMaxTimeout);
427      }
428  
429      // ------------------------------------------------------------
430      //  Path MTU
431      //
432      private final int iMinMTU = 128; // used for some ppp connections
433      private final int iMaxMTU = 8192; // used on some LANs
434  
getMTU()435      int getMTU() {
436  	return getIntProperty("net.slp.MTU",
437  			      Defaults.iMTU,
438  			      iMinMTU,
439  			      iMaxMTU);
440      }
441  
442  
443      // ------------------------------------------------------------
444      // Serialized registrations.
445      //
446  
getSerializedRegURL()447      String getSerializedRegURL() {
448  
449  	return System.getProperty("net.slp.serializedRegURL", null);
450  
451      }
452  
453      // ------------------------------------------------------------
454      // Are we running as a DA or SA server?
455      //
456  
457      protected static boolean isSA = false;
458  
isDA()459      boolean isDA() {
460  	return false;
461      }
462  
isSA()463      boolean isSA() {
464  	return isSA;
465      }
466  
467      // ------------------------------------------------------------
468      // DA and SA attributes
469      //
470  
getDAAttributes()471      Vector getDAAttributes() {
472  	return getAttributes("net.slp.DAAttributes",
473  			     Defaults.defaultDAAttributes,
474  			     true);
475      }
476  
getSAAttributes()477      Vector getSAAttributes() {
478  	return getAttributes("net.slp.SAAttributes",
479  			     Defaults.defaultSAAttributes,
480  			     false);
481      }
482  
getAttributes(String prop, Vector defaults, boolean daAttrs)483      private Vector getAttributes(String prop,
484  				 Vector defaults,
485  				 boolean daAttrs) {
486  	String attrList =
487  	    System.getProperty(prop);
488  
489  	if (attrList == null || attrList.length() <= 0) {
490  	    return (Vector)defaults.clone();
491  
492  	}
493  
494  	try {
495  	    Vector sAttrs =
496  		SrvLocHeader.parseCommaSeparatedListIn(attrList, false);
497  
498  	    Vector attrs = new Vector();
499  	    int i, n = sAttrs.size();
500  
501  	    // Create attribute objects.
502  
503  	    for (i = 0; i < n; i++) {
504  		String attrExp = (String)sAttrs.elementAt(i);
505  		ServiceLocationAttribute attr =
506  		    new ServiceLocationAttribute(attrExp, false);
507  
508  		// If this is the min-refresh-interval, then check the value.
509  
510  		if (daAttrs &&
511  		    attr.getId().equals(
512  				Defaults.MIN_REFRESH_INTERVAL_ATTR_ID)) {
513  		    Vector values = attr.getValues();
514  		    boolean errorp = true;
515  
516  		    if (values != null && values.size() == 1) {
517  			Object val = values.elementAt(0);
518  
519  			if (val instanceof Integer) {
520  			    int ival = ((Integer)val).intValue();
521  
522  			    if (ival >= 0 &&
523  				ival <= ServiceURL.LIFETIME_MAXIMUM) {
524  				errorp = false;
525  
526  			    }
527  			}
528  		    }
529  
530  		    // Throw exception if it didn't work.
531  
532  		    if (errorp) {
533  			throw new ServiceLocationException(
534  				ServiceLocationException.PARSE_ERROR,
535  				"syntax_error_prop",
536  				new Object[] {prop, attrs});
537  
538  		    }
539  		}
540  
541  		// Add attribute to vector.
542  
543  		attrs.addElement(attr);
544  
545  	    }
546  
547  	    return attrs;
548  
549  	} catch (Exception ex) {
550  
551  	    writeLog("syntax_error_prop",
552  		     new Object[] {prop, attrList});
553  	    return (Vector)defaults.clone();
554  
555  	}
556      }
557  
558      // -------------------------------------------------------------
559      // Do we support V1?
560      //
561  
isV1Supported()562      boolean isV1Supported() {
563  	return false;
564      }
565  
566      // -------------------------------------------------------------
567      // Queue length for server socket.
568      //
569  
getServerSocketQueueLength()570      int getServerSocketQueueLength() {
571  	return getIntProperty("sun.net.slp.serverSocketQueueLength",
572  			      Defaults.iSocketQueueLength,
573  			      0,
574  			      Integer.MAX_VALUE);
575      }
576  
577      // ------------------------------------------------------------
578      // Testing options
579      //
580  
581  
traceAll()582      boolean traceAll() {// not official!
583  	return Boolean.getBoolean("sun.net.slp.traceALL");
584      }
585  
regTest()586      boolean regTest() {
587  	if (Boolean.getBoolean("sun.net.slp.traceALL") ||
588  	    Boolean.getBoolean("net.slp.traceReg"))
589  	    return true;
590  	else
591  	    return false;
592      }
593  
traceMsg()594      boolean traceMsg() {
595  	if (Boolean.getBoolean("sun.net.slp.traceALL") ||
596  	    Boolean.getBoolean("net.slp.traceMsg"))
597  	    return true;
598  	else
599  	    return false;
600      }
601  
traceDrop()602      boolean traceDrop() {
603  	if (Boolean.getBoolean("sun.net.slp.traceALL") ||
604  	    Boolean.getBoolean("net.slp.traceDrop"))
605  	    return true;
606  	else
607  	    return false;
608      }
609  
traceDATraffic()610      boolean traceDATraffic() {
611  	if (Boolean.getBoolean("sun.net.slp.traceALL") ||
612  	    Boolean.getBoolean("net.slp.traceDATraffic"))
613  	    return true;
614  	else
615  	    return false;
616      }
617  
618      // cannot use Boolean.getBoolean as the default is 'true'
619      // using that mechanism, absense would be considered 'false'
620  
passiveDADetection()621      boolean passiveDADetection() {
622  
623  	String sPassive =
624  	    System.getProperty("net.slp.passiveDADetection", "true");
625  	if (sPassive.equalsIgnoreCase("true"))
626  	    return true;
627  	else
628  	    return false;
629  
630      }
631  
632      // Initialized when the SLPConfig object is created to avoid changing
633      //  during the program.
634      private boolean broadcastOnly = false;
635  
isBroadcastOnly()636      boolean isBroadcastOnly() {
637  	return broadcastOnly;
638      }
639  
640  
641      // ------------------------------------------------------------
642      // Multicast/broadcast socket mangement.
643      //
644      DatagramSocket broadSocket = null;   // cached broadcast socket.
645  
646  
647      // Reopen the multicast/broadcast socket bound to the
648      //  interface. If groups is not null, then join all
649      //  the groups. Otherwise, this is send only.
650  
651      DatagramSocket
refreshMulticastSocketOnInterface(InetAddress interfac, Vector groups)652  	refreshMulticastSocketOnInterface(InetAddress interfac,
653  					  Vector groups) {
654  
655  	try {
656  
657  	    // Reopen it.
658  
659  	    DatagramSocket dss =
660  		getMulticastSocketOnInterface(interfac,
661  					      (groups == null ? true:false));
662  
663  	    if ((groups != null) && (dss instanceof MulticastSocket)) {
664  		int i, n = groups.size();
665  		MulticastSocket mss = (MulticastSocket)dss;
666  
667  		for (i = 0; i < n; i++) {
668  		    InetAddress maddr = (InetAddress)groups.elementAt(i);
669  
670  		    mss.joinGroup(maddr);
671  
672  		}
673  	    }
674  
675  	    return dss;
676  
677  	} catch (Exception ex) {
678  
679  	    // Any exception in error recovery causes program to die.
680  
681  	    Assert.slpassert(false,
682  			  "cast_socket_failure",
683  			  new Object[] {ex, ex.getMessage()});
684  
685  	}
686  
687  	return null;
688      }
689  
690      // Open a multicast/broadcast socket on the interface. Note that if
691      //  the socket is broadcast, the network interface is not specified in the
692      //  creation message. Is it bound to all interfaces? The isSend parameter
693      //  specifies whether the socket is for send only.
694  
695      DatagramSocket
getMulticastSocketOnInterface(InetAddress interfac, boolean isSend)696  	getMulticastSocketOnInterface(InetAddress interfac, boolean isSend)
697  	throws ServiceLocationException {
698  
699  	DatagramSocket castSocket = null;
700  
701  	// Substitute broadcast if we are configured for it.
702  
703  	if (isBroadcastOnly()) {
704  
705  	    try {
706  
707  		// If transmit, then simply return a new socket.
708  
709  		if (isSend) {
710  		    castSocket = new DatagramSocket();
711  
712  		} else {
713  
714  		    // Return cached socket if there.
715  
716  		    if (broadSocket != null) {
717  			castSocket = broadSocket;
718  
719  		    } else {
720  
721  			// Make a new broadcast socket.
722  
723  			castSocket =
724  			    new DatagramSocket(Defaults.iSLPPort,
725  					       getBroadcastAddress());
726  
727  		    }
728  
729  		    // Cache for future reference.
730  
731  		    broadSocket = castSocket;
732  		}
733  	    } catch (SocketException ex) {
734  		throw
735  		    new ServiceLocationException(
736  				ServiceLocationException.NETWORK_INIT_FAILED,
737  				"socket_creation_failure",
738  				new Object[] {
739  			getBroadcastAddress(), ex.getMessage()});
740  	    }
741  
742  	} else {
743  
744  	    // Create a multicast socket.
745  
746  	    MulticastSocket ms;
747  
748  	    try {
749  
750  		if (isSend) {
751  		    ms = new MulticastSocket();
752  
753  		} else {
754  		    ms = new MulticastSocket(Defaults.iSLPPort);
755  
756  		}
757  
758  	    } catch (IOException ex) {
759  		throw
760  		    new ServiceLocationException(
761  				ServiceLocationException.NETWORK_INIT_FAILED,
762  				"socket_creation_failure",
763  				new Object[] {interfac, ex.getMessage()});
764  	    }
765  
766  
767  	    try {
768  
769  		// Set the TTL and the interface on the multicast socket.
770  		//  Client is responsible for joining group.
771  
772  		ms.setTimeToLive(getMCRadius());
773  		ms.setInterface(interfac);
774  
775  	    } catch (IOException ex) {
776  		throw
777  		    new ServiceLocationException(
778  				ServiceLocationException.NETWORK_INIT_FAILED,
779  				"socket_initializtion_failure",
780  				new Object[] {interfac, ex.getMessage()});
781  	    }
782  
783  	    castSocket = ms;
784  
785  	}
786  
787  	return castSocket;
788      }
789  
790      // ------------------------------------------------------------
791      // Type hint
792      //
793  
794      // Return a vector of ServiceType objects for the type hint.
795  
getTypeHint()796      Vector getTypeHint() {
797  	Vector hint = new Vector();
798  	String sTypeList = System.getProperty("net.slp.typeHint", "");
799  
800  	if (sTypeList.length() <= 0) {
801  	    return hint;
802  
803  	}
804  
805  	// Create a vector of ServiceType objects for the type hint.
806  
807  	try {
808  
809  	    hint = SrvLocHeader.parseCommaSeparatedListIn(sTypeList, true);
810  
811  	    int i, n = hint.size();
812  
813  	    for (i = 0; i < n; i++) {
814  		String type = (String)hint.elementAt(i);
815  
816  		hint.setElementAt(new ServiceType(type), i);
817  
818  	    }
819  	} catch (ServiceLocationException ex) {
820  
821  	    writeLog("syntax_error_prop",
822  		     new Object[] {"net.slp.typeHint", sTypeList});
823  
824  	    hint.removeAllElements();
825  
826  	}
827  
828  	return hint;
829  
830      }
831  
832      // ------------------------------------------------------------
833      // Configured scope handling
834      //
835  
836      // Vector of configured scopes.
837  
838      private Vector configuredScopes = null;
839  
840      // Vector of configures scopes for SA.
841  
842      private Vector saConfiguredScopes = null;
843  
844      // Vector of scopes only in the sa server.
845  
846      private Vector saOnlyScopes = null;
847  
848      // Return the configured scopes.
849  
getConfiguredScopes()850      Vector getConfiguredScopes() {
851  	return (Vector)configuredScopes.clone();
852      }
853  
854      // Return SA scopes.
855  
getSAOnlyScopes()856      Vector getSAOnlyScopes() {
857  	return (Vector)saOnlyScopes.clone();
858  
859      }
860  
861      // Return the configured scopes for the SA.
862  
getSAConfiguredScopes()863      Vector getSAConfiguredScopes() {
864  	return (Vector)saConfiguredScopes.clone();
865  
866      }
867  
868      // Add scopes discovered during preconfigured DA contact.
869      //  These count as configured scopes.
870  
addPreconfiguredDAScopes(Vector scopes)871      void addPreconfiguredDAScopes(Vector scopes) {
872  
873  	int i, n = scopes.size();
874  
875  	for (i = 0; i < n; i++) {
876  	    Object scope = scopes.elementAt(i);
877  
878  	    if (!configuredScopes.contains(scope)) {
879  		configuredScopes.addElement(scope);
880  
881  	    }
882  
883  	    // There better be none extra here for the SA server/DA.
884  
885  	    if (isSA() || isDA()) {
886  		Assert.slpassert(saConfiguredScopes.contains(scope),
887  			      "sa_new_scope",
888  			      new Object[] {scope, saConfiguredScopes});
889  
890  	    }
891  	}
892      }
893  
894      // Initialize the scopes list on property.
895  
initializeScopes(String prop)896      private Vector initializeScopes(String prop) {
897  
898  	String sScopes = System.getProperty(prop);
899  
900  	if (sScopes == null || sScopes.length() <= 0) {
901  	    return new Vector();
902  	}
903  
904  	try {
905  
906  	    Vector vv =
907  		SrvLocHeader.parseCommaSeparatedListIn(sScopes, true);
908  
909  	    // Unescape scope strings.
910  
911  	    SLPHeaderV2.unescapeScopeStrings(vv);
912  
913  	    // Validate, lower case scope names.
914  
915  	    DATable.validateScopes(vv, getLocale());
916  
917  	    if (vv.size() > 0) {
918  		return vv;
919  	    }
920  
921  	} catch (ServiceLocationException ex) {
922  	    writeLog("syntax_error_prop",
923  		     new Object[] {
924  		prop,
925  		    sScopes});
926  
927  
928  	}
929  
930  	return new Vector();
931      }
932  
933      // Vector of preconfigured DAs. Read only after initialized.
934  
935      private Vector preconfiguredDAs = null;
936  
937      // Return a vector of DA addresses.
938  
getPreconfiguredDAs()939      Vector getPreconfiguredDAs() {
940  	return (Vector)preconfiguredDAs.clone();
941  
942      }
943  
944      // Initialize preconfigured DA list.
945  
initializePreconfiguredDAs()946      private Vector initializePreconfiguredDAs() {
947  	String sDAList = System.getProperty("net.slp.DAAddresses", "");
948  	Vector ret = new Vector();
949  
950  	sDAList.trim();
951  
952  	if (sDAList.length() <= 0) {
953  	    return ret;
954  
955  	}
956  
957  	try {
958  
959  	    ret = SrvLocHeader.parseCommaSeparatedListIn(sDAList, true);
960  
961  	} catch (ServiceLocationException ex) {
962  
963  	    writeLog("syntax_error_prop",
964  		     new Object[] {"net.slp.DAAddress", sDAList});
965  
966  	    return ret;
967  
968  	}
969  
970  	// Convert to InetAddress objects.
971  
972  	int i;
973  
974  	for (i = 0; i < ret.size(); i++) {
975  	    String da = "";
976  
977  	    try {
978  		da = ((String)ret.elementAt(i)).trim();
979  		InetAddress daAddr = InetAddress.getByName(da);
980  
981  		ret.setElementAt(daAddr, i);
982  
983  	    } catch (UnknownHostException ex) {
984  
985  		writeLog("resolve_failed",
986  			 new Object[] {da});
987  
988  		/*
989  		 *  Must decrement the index 'i' otherwise the next iteration
990  		 *  around the loop will miss the element immediately after
991  		 *  the element removed.
992  		 *
993  		 *  WARNING: Do not use 'i' again until the loop has
994  		 *           iterated as it may, after decrementing,
995  		 *           be negative.
996  		 */
997  		ret.removeElementAt(i);
998  		i--;
999  		continue;
1000  	    }
1001  	}
1002  
1003  
1004  	return ret;
1005      }
1006  
1007      // ------------------------------------------------------------
1008      // SLPv1 Support Switches
1009      //
1010  
getSLPv1NotSupported()1011      boolean getSLPv1NotSupported() {// not official!
1012  	return Boolean.getBoolean("sun.net.slp.SLPv1NotSupported");
1013  
1014      }
1015  
getAcceptSLPv1UnscopedRegs()1016      boolean getAcceptSLPv1UnscopedRegs() {// not official!
1017  
1018  	if (!getSLPv1NotSupported()) {
1019  	    return Boolean.getBoolean("sun.net.slp.acceptSLPv1UnscopedRegs");
1020  
1021  	}
1022  
1023  	return false;
1024      }
1025  
1026      // ------------------------------------------------------------
1027      // Accessor for SLPConfig object
1028      //
1029  
1030      protected static SLPConfig theSLPConfig = null;
1031  
getSLPConfig()1032      static SLPConfig getSLPConfig() {
1033  
1034  	if (theSLPConfig == null) {
1035  	    theSLPConfig = new SLPConfig();
1036  	}
1037  
1038  	return theSLPConfig;
1039  
1040      }
1041  
1042      /**
1043       * @return Maximum number of messages/objects to return.
1044       */
1045  
getMaximumResults()1046      int getMaximumResults()  {
1047  	int i = Integer.getInteger("net.slp.maxResults",
1048  				   Defaults.iMaximumResults).intValue();
1049  	if (i == -1) {
1050  	    i = Integer.MAX_VALUE;
1051  
1052  	}
1053  
1054  	if (OKBound(i, 1, Integer.MAX_VALUE)) {
1055  	    return i;
1056  
1057  	} else {
1058  
1059  	    writeLog("bad_prop_tag",
1060  		     new Object[] {
1061  		"net.slp.maxResults"});
1062  
1063  	    return Defaults.iMaximumResults;
1064  
1065  	}
1066      }
1067  
1068      /**
1069       * Convert a language tag into a locale.
1070       */
1071  
langTagToLocale(String ltag)1072      static Locale langTagToLocale(String ltag) {
1073  
1074  	// We treat the first part as the ISO 639 language and the
1075  	// second part as the ISO 3166 country tag, even though RFC
1076  	// 1766 doesn't necessarily require that. We should probably
1077  	// use a lookup table here to determine if they are correct.
1078  
1079  	StringTokenizer tk = new StringTokenizer(ltag, "-");
1080  	String lang = "";
1081  	String country = "";
1082  
1083  	if (tk.hasMoreTokens()) {
1084  	    lang = tk.nextToken();
1085  
1086  	    if (tk.hasMoreTokens()) {
1087  		country = tk.nextToken("");
1088  					// country name may have "-" in it...
1089  
1090  	    }
1091  	}
1092  
1093  	return new Locale(lang, country);
1094      }
1095  
1096      /**
1097       * Convert a Locale object into a language tag for output.
1098       *
1099       * @param locale The Locale.
1100       * @return String with the language tag encoded.
1101       */
1102  
localeToLangTag(Locale locale)1103      static String localeToLangTag(Locale locale) {
1104  
1105  	// Construct the language tag.
1106  
1107  	String ltag = locale.getCountry();
1108  	ltag = locale.getLanguage() + (ltag.length() <= 0 ? "" : ("-" + ltag));
1109  
1110  	return ltag;
1111  
1112      }
1113  
1114      /**
1115       * @return the language requests will be made in.
1116       */
getLocale()1117      static Locale  getLocale()    {
1118  	String s = System.getProperty("net.slp.locale");
1119  
1120  	if (s != null && s.length() > 0) {
1121  	    return langTagToLocale(s);
1122  
1123  	} else {
1124  
1125  	    // Return the Java default if the SLP property is not set.
1126  
1127  	    return Locale.getDefault();
1128  
1129  	}
1130      }
1131  
1132      /**
1133       * @return the InetAddress of the broadcast interface.
1134       */
1135  
1136      static private InetAddress broadcastAddress;
1137  
getBroadcastAddress()1138      static InetAddress getBroadcastAddress() {
1139  	if (broadcastAddress == null) {
1140  
1141  	    try {
1142  		broadcastAddress =
1143  		    InetAddress.getByName(Defaults.sBroadcast);
1144  	    } catch (UnknownHostException uhe) {
1145  
1146  		Assert.slpassert(false,
1147  			      "cast_address_failure",
1148  			      new Object[] {Defaults.sBroadcast});
1149  
1150  	    }
1151  	}
1152  	return broadcastAddress;
1153      }
1154  
1155  
1156      /**
1157       * @return the InetAddress of the multicast group.
1158       */
1159  
1160      static private InetAddress multicastAddress;
1161  
getMulticastAddress()1162      static InetAddress getMulticastAddress() {
1163  	if (multicastAddress == null) {
1164  
1165  	    try {
1166  		multicastAddress =
1167  		    InetAddress.getByName(Defaults.sGeneralSLPMCAddress);
1168  	    } catch (UnknownHostException uhe) {
1169  		Assert.slpassert(false,
1170  			      "cast_address_failure",
1171  			      new Object[] {Defaults.sGeneralSLPMCAddress});
1172  
1173  	    }
1174  	}
1175  	return multicastAddress;
1176      }
1177  
1178      /**
1179       * @return the interfaces on which SLP should listen and transmit.
1180       */
1181  
1182      private static Vector interfaces = null;
1183  
getInterfaces()1184      Vector getInterfaces() {
1185  
1186  	if (interfaces == null) {
1187  	    InetAddress iaLocal = null;
1188  
1189  	    // Get local host.
1190  
1191  	    try {
1192  		iaLocal =  InetAddress.getLocalHost();
1193  
1194  	    }  catch (UnknownHostException ex) {
1195  		Assert.slpassert(false,
1196  			      "resolve_failed",
1197  			      new Object[] {"localhost"});
1198  	    }
1199  
1200  	    String mcastI = System.getProperty("net.slp.interfaces");
1201  	    interfaces = new Vector();
1202  
1203  	    // Only add local host if nothing else is given.
1204  
1205  	    if (mcastI == null || mcastI.length() <= 0) {
1206  		interfaces.addElement(iaLocal);
1207  		return interfaces;
1208  
1209  	    }
1210  
1211  	    Vector nintr;
1212  
1213  	    try {
1214  
1215  		nintr = SrvLocHeader.parseCommaSeparatedListIn(mcastI, true);
1216  
1217  	    } catch (ServiceLocationException ex) {
1218  		writeLog("syntax_error_prop",
1219  			 new Object[] {
1220  		    "net.slp.multicastInterfaces",
1221  			mcastI});
1222  
1223  		// Add local host.
1224  
1225  		interfaces.addElement(iaLocal);
1226  
1227  		return interfaces;
1228  
1229  	    }
1230  
1231  	    // See if they are really there.
1232  
1233  	    int i, n = nintr.size();
1234  
1235  	    for (i = 0; i < n; i++) {
1236  		InetAddress ia;
1237  		String host = (String)nintr.elementAt(i);
1238  
1239  		try {
1240  
1241  		    ia = InetAddress.getByName(host);
1242  
1243  		} catch (UnknownHostException ex) {
1244  		    writeLog("unknown_interface",
1245  			     new Object[] {host,
1246  					       "net.slp.multicastInterfaces"});
1247  		    continue;
1248  
1249  		}
1250  
1251  		if (!interfaces.contains(ia)) {
1252  
1253  		    // Add default at beginning.
1254  
1255  		    if (ia.equals(iaLocal)) {
1256  			interfaces.insertElementAt(ia, 0);
1257  
1258  		    } else {
1259  			interfaces.addElement(ia);
1260  
1261  		    }
1262  		}
1263  	    }
1264  	}
1265  
1266  	return interfaces;
1267  
1268      }
1269  
1270      /**
1271       * @return An InetAddress object representing 127.0.0.1
1272       */
getLoopback()1273      InetAddress getLoopback() {
1274  	InetAddress iaLoopback = null;
1275  
1276  	try {
1277  	    iaLoopback = InetAddress.getByName(Defaults.LOOPBACK_ADDRESS);
1278  
1279  	}  catch (UnknownHostException ex) {
1280  	    Assert.slpassert(false,
1281  			  "resolve_failed",
1282  			  new Object[] {"localhost loopback"});
1283  	}
1284  
1285  	return iaLoopback;
1286      }
1287  
1288      /**
1289       * @return The default interface, which should be the first in the
1290       *         interfaces vector Vector.
1291       */
1292  
getLocalHost()1293      InetAddress getLocalHost() {
1294  	Vector inter = getInterfaces();
1295  	return (InetAddress)inter.elementAt(0);
1296  
1297      }
1298  
1299      // Return true if the address is one of the local interfaces.
1300  
isLocalHostSource(InetAddress addr)1301      boolean isLocalHostSource(InetAddress addr) {
1302  
1303  	// First check loopback
1304  
1305  	if (addr.equals(getLoopback())) {
1306  	    return true;
1307  
1308  	}
1309  
1310  	return interfaces.contains(addr);
1311  
1312      }
1313  
1314      // -----------------
1315      // Timeouts
1316      //
1317  
1318      // Return the maximum wait for multicast convergence.
1319  
1320      final static private int iMultiMin = 1000;  // one second
1321      final static private int iMultiMax = 60000; // one minute
1322  
getMulticastMaximumWait()1323      int getMulticastMaximumWait() {
1324  
1325  	return getIntProperty("net.slp.multicastMaximumWait",
1326  			      Defaults.iMulticastMaxWait,
1327  			      iMultiMin,
1328  			      iMultiMax);
1329      }
1330  
1331      /*
1332       * @return Vector of timeouts for multicast convergence.
1333       */
1334  
getMulticastTimeouts()1335      int[] getMulticastTimeouts() {
1336  	int[] timeouts = parseTimeouts("net.slp.multicastTimeouts",
1337  			     Defaults.a_iConvergeTimeout);
1338  
1339  	timeouts = capTimeouts("net.slp.multicastTimeouts",
1340  			       timeouts,
1341  			       false,
1342  			       0,
1343  			       0);
1344  
1345  	return timeouts;
1346      }
1347  
1348      /**
1349       * @return Vector of timeouts to try for datagram transmission.
1350       */
1351  
getDatagramTimeouts()1352      int[] getDatagramTimeouts() {
1353  	int[] timeouts = parseTimeouts("net.slp.datagramTimeouts",
1354  			     Defaults.a_iDatagramTimeout);
1355  
1356  	timeouts = capTimeouts("net.slp.datagramTimeouts",
1357  			       timeouts,
1358  			       true,
1359  			       iMultiMin,
1360  			       iMultiMax);
1361  
1362  	return timeouts;
1363      }
1364  
1365      /**
1366       * @return Vector of timeouts for DA discovery multicast.
1367       */
1368  
getDADiscoveryTimeouts()1369      int[] getDADiscoveryTimeouts() {
1370  	int[] timeouts = parseTimeouts("net.slp.DADiscoveryTimeouts",
1371  			     Defaults.a_iDADiscoveryTimeout);
1372  
1373  	timeouts = capTimeouts("net.slp.DADiscoveryTimeouts",
1374  				timeouts,
1375  				false,
1376  				0,
1377  				0);
1378  
1379  	return timeouts;
1380      }
1381  
1382      /**
1383       *  This method ensures that all the timeouts are within valid ranges.
1384       *  The sum of all timeouts for the given property name must not
1385       *  exceed the value returned by <i>getMulticastMaximumWait()</i>. If
1386       *  the sum of all timeouts does exceed the maximum wait period the
1387       *  timeouts are averaged out so that the sum equals the maximum wait
1388       *  period.
1389       *	<br>
1390       *  Additional range checking is also performed when <i>rangeCheck</i>
1391       *  is true. Then the sum of all timeouts must also be between <i>min</i>
1392       *  and <i>max</i>. If the sum of all timeouts is not within the range
1393       *  the average is taken from the closest range boundary.
1394       *
1395       *  @param property
1396       *	    Name of timeout property being capped. This is only present for
1397       *	    reporting purposes and no actual manipulation of the property
1398       *      is made within this method.
1399       *  @param timeouts
1400       *      Array of timeout values.
1401       *  @param rangeCheck
1402       *      Indicator of whether additional range checking is required. When
1403       *      false <i>min</i> and <i>max</i> are ignored.
1404       *  @param min
1405       *      Additional range checking lower boundary.
1406       *  @param max
1407       *      Additional range checking upper boundary.
1408       *  @return
1409       *      Array of capped timeouts. Note this may be the same array as
1410       *      passed in (<i>timeouts</i>).
1411       */
capTimeouts(String property, int[] timeouts, boolean rangeCheck, int min, int max)1412      private int[] capTimeouts(String property,
1413  			      int[] timeouts,
1414  			      boolean rangeCheck,
1415  			      int min,
1416  			      int max) {
1417  
1418  	int averagedTimeout;
1419  	int totalWait = 0;
1420  
1421  	for (int index = 0; index < timeouts.length; index++) {
1422  	    totalWait += timeouts[index];
1423  	}
1424  
1425  	if (rangeCheck) {
1426  	    // If sum of timeouts within limits then finished.
1427  	    if (totalWait >= min && totalWait <= max) {
1428  		return timeouts;
1429  	    }
1430  
1431  	    // Average out the timeouts so the sum is equal to the closest
1432  	    // range boundary.
1433  	    if (totalWait < min) {
1434  		averagedTimeout = min / timeouts.length;
1435  	    } else {
1436  		averagedTimeout = max / timeouts.length;
1437  	    }
1438  
1439  	    writeLog("capped_range_timeout_prop",
1440  		     new Object[] {property,
1441  				   String.valueOf(totalWait),
1442  				   String.valueOf(min),
1443  				   String.valueOf(max),
1444  				   String.valueOf(timeouts.length),
1445  				   String.valueOf(averagedTimeout)});
1446  	} else {
1447  	    // Sum of all timeouts must not exceed this value.
1448  	    int maximumWait = getMulticastMaximumWait();
1449  
1450  	    // If sum of timeouts within limits then finished.
1451  	    if (totalWait <= maximumWait) {
1452  		return timeouts;
1453  	    }
1454  
1455  	    // Average out the timeouts so the sum is equal to the maximum
1456  	    // timeout.
1457  	    averagedTimeout = maximumWait / timeouts.length;
1458  
1459  	    writeLog("capped_timeout_prop",
1460  		     new Object[] {property,
1461  				   String.valueOf(totalWait),
1462  				   String.valueOf(maximumWait),
1463  				   String.valueOf(timeouts.length),
1464  				   String.valueOf(averagedTimeout)});
1465  	}
1466  
1467  	for (int index = 0; index < timeouts.length; index++) {
1468  	    timeouts[index] = averagedTimeout;
1469  	}
1470  
1471  	return timeouts;
1472      }
1473  
parseTimeouts(String property, int[] defaults)1474      private int[] parseTimeouts(String property, int[] defaults) {
1475  
1476  	String sTimeouts = System.getProperty(property);
1477  
1478  	if (sTimeouts == null || sTimeouts.length() <= 0) {
1479  	    return defaults;
1480  
1481  	}
1482  
1483  	Vector timeouts = null;
1484  
1485  	try {
1486  	    timeouts = SrvLocHeader.parseCommaSeparatedListIn(sTimeouts, true);
1487  
1488  	} catch (ServiceLocationException ex) {
1489  	    writeLog("syntax_error_prop",
1490  		     new Object[] {property, sTimeouts});
1491  	    return defaults;
1492  
1493  	}
1494  
1495  	int iCount = 0;
1496  	int[] iTOs = new int[timeouts.size()];
1497  
1498  	for (Enumeration en = timeouts.elements(); en.hasMoreElements(); ) {
1499  	    String s1 = (String)en.nextElement();
1500  
1501  	    try {
1502  		iTOs[iCount] = Integer.parseInt(s1);
1503  
1504  	    }	catch (NumberFormatException nfe) {
1505  		writeLog("syntax_error_prop",
1506  			 new Object[] {property, sTimeouts});
1507  		return defaults;
1508  
1509  	    }
1510  
1511  	    if (iTOs[iCount] < 0) {
1512  		writeLog("invalid_timeout_prop",
1513  			 new Object[] {property, String.valueOf(iTOs[iCount])});
1514  		return defaults;
1515  	    }
1516  
1517  	    iCount++;
1518  	}
1519  
1520  	return iTOs;
1521      }
1522  
1523      // -----------------------------
1524      // SLP Time Calculation
1525      //
1526  
1527      /**
1528       * Returns the number of seconds since 00:00 Universal Coordinated
1529       * Time, January 1, 1970.
1530       *
1531       * Java returns the number of milliseconds, so all the method does is
1532       * divide by 1000.
1533       *
1534       * This implementation still will have a problem when the Java time
1535       * values wraps, but there isn't much we can do now.
1536       */
currentSLPTime()1537      static long currentSLPTime() {
1538  	return (System.currentTimeMillis() / 1000);
1539      }
1540  
1541      /* security */
1542  
1543      // Indicates whether security class is available.
1544  
getSecurityEnabled()1545      boolean getSecurityEnabled() {
1546  	return securityEnabled;
1547  
1548      }
1549  
1550      private static boolean securityEnabled;
1551  
1552      // Indicates whether the securityEnabled property is true
1553  
getHasSecurity()1554      boolean getHasSecurity() {
1555  	return securityEnabled &&
1556  	    (new Boolean(System.getProperty("net.slp.securityEnabled",
1557  					    "false")).booleanValue());
1558      }
1559  
1560      // I18N Support.
1561  
1562      private static final String BASE_BUNDLE_NAME = "com/sun/slp/ClientLib";
1563  
getMessageBundle(Locale locale)1564      ResourceBundle getMessageBundle(Locale locale) {
1565  
1566  	ResourceBundle msgBundle = null;
1567  
1568  	// First try the Solaris Java locale area
1569  
1570  	try {
1571  	    URL[] urls = new URL[] {new URL("file:/usr/share/lib/locale/")};
1572  
1573  	    URLClassLoader ld = new URLClassLoader(urls);
1574  
1575  	    msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale, ld);
1576  
1577  	    return msgBundle;
1578  	} catch (MalformedURLException e) {	// shouldn't get here
1579  	} catch (MissingResourceException ex) {
1580  	    System.err.println("Missing resource bundle ``"+
1581  			       "/usr/share/lib/locale/" + BASE_BUNDLE_NAME +
1582  			       "'' for locale ``" +
1583  			       locale + "''; trying default...");
1584  	}
1585  
1586  	try {
1587  	    msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale);
1588  
1589  	} catch (MissingResourceException ex) {  // can't localize this one!
1590  
1591  	    // We can't print out to the log, because we may be in the
1592  	    //  process of trying to.
1593  
1594  	    System.err.println("Missing resource bundle ``"+
1595  			       BASE_BUNDLE_NAME+
1596  			       "'' for locale ``"+
1597  			       locale+
1598  			       "''");
1599  	    // Hosed if the default locale is missing.
1600  
1601  	    if (locale.equals(Defaults.locale)) {
1602  
1603  		System.err.println("Exiting...");
1604  		System.exit(1);
1605  	    }
1606  
1607  	    // Otherwise, return the default locale.
1608  
1609  	    System.err.println("Using SLP default locale ``" +
1610  			       Defaults.locale +
1611  			       "''");
1612  
1613  	    msgBundle = getMessageBundle(Defaults.locale);
1614  
1615  	}
1616  
1617  	return msgBundle;
1618      }
1619  
formatMessage(String msgTag, Object[] params)1620      String formatMessage(String msgTag, Object[] params) {
1621  	ResourceBundle bundle = getMessageBundle(getLocale());
1622  	return formatMessageInternal(msgTag, params, bundle);
1623  
1624      }
1625  
1626      // MessageFormat is picky about types. Convert the params into strings.
1627  
convertToString(Object[] params)1628      static void convertToString(Object[] params) {
1629  	int i, n = params.length;
1630  
1631  	for (i = 0; i < n; i++) {
1632  
1633  	    if (params[i] != null) {
1634  		params[i] = params[i].toString();
1635  
1636  	    } else {
1637  		params[i] = "<null>";
1638  
1639  	    }
1640  	}
1641      }
1642  
1643      static String
formatMessageInternal(String msgTag, Object[] params, ResourceBundle bundle)1644  	formatMessageInternal(String msgTag,
1645  			      Object[] params,
1646  			      ResourceBundle bundle) {
1647  	String pattern = "";
1648  
1649  	try {
1650  	    pattern = bundle.getString(msgTag);
1651  
1652  	} catch (MissingResourceException ex) {
1653  
1654  	    // Attempt to report error. Can't use Assert here because it
1655  	    //  calls back into SLPConfig.
1656  	    String msg = "Can''t find message ``{0}''''.";
1657  
1658  	    try {
1659  		pattern = bundle.getString("cant_find_resource");
1660  		msg = MessageFormat.format(pattern, new Object[] {msgTag});
1661  
1662  	    } catch (MissingResourceException exx) {
1663  
1664  	    }
1665  
1666  	    System.err.println(msg);
1667  	    System.exit(-1);
1668  	}
1669  
1670  	convertToString(params);
1671  
1672  	return MessageFormat.format(pattern, params);
1673      }
1674  
1675      // logging.
1676  
1677      // Protected so slpd can replace it.
1678  
1679      protected Writer log;
1680  
1681      // Synchronized so writes from multiple threads don't get interleaved.
1682  
writeLog(String msgTag, Object[] params)1683      void writeLog(String msgTag, Object[] params) {
1684  
1685  	// MessageFormat is picky about types. Convert the params into strings.
1686  
1687  	convertToString(params);
1688  
1689  	try {
1690  	    synchronized (log) {
1691  		log.write(formatMessage(msgTag, params));
1692  		log.flush();
1693  	    }
1694  	} catch (IOException ex) {}
1695      }
1696  
writeLogLine(String msgTag, Object[] params)1697      void writeLogLine(String msgTag, Object[] params) {
1698  
1699  	try {
1700  	    String pattern = getMessageBundle(getLocale()).getString(msgTag);
1701  
1702  	    synchronized (log) {
1703  		log.write(formatMessage(msgTag, params));
1704  		log.write("\n");
1705  		log.flush();
1706  	    }
1707  	} catch (IOException ex) {}
1708  
1709      }
1710  
getDateString()1711      static String getDateString() {
1712  
1713  	DateFormat df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
1714  						       DateFormat.DEFAULT,
1715  						       getLocale());
1716  	Calendar calendar = Calendar.getInstance(getLocale());
1717  	return df.format(calendar.getTime());
1718  
1719      }
1720  
1721  
1722      // On load, check whether the signature class is available, and turn
1723      //  security off if not.
1724  
1725      static {
1726  
1727  	securityEnabled = true;
1728  	try {
1729  	    Class c = Class.forName("com.sun.slp.AuthBlock");
1730  
1731  	} catch (ClassNotFoundException e) {
1732  	    securityEnabled = false;
1733  	}
1734      }
1735  
1736  }
1737