xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPConfig.java (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
459     boolean isDA() {
460 	return false;
461     }
462 
463     boolean isSA() {
464 	return isSA;
465     }
466 
467     // ------------------------------------------------------------
468     // DA and SA attributes
469     //
470 
471     Vector getDAAttributes() {
472 	return getAttributes("net.slp.DAAttributes",
473 			     Defaults.defaultDAAttributes,
474 			     true);
475     }
476 
477     Vector getSAAttributes() {
478 	return getAttributes("net.slp.SAAttributes",
479 			     Defaults.defaultSAAttributes,
480 			     false);
481     }
482 
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 
562     boolean isV1Supported() {
563 	return false;
564     }
565 
566     // -------------------------------------------------------------
567     // Queue length for server socket.
568     //
569 
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 
582     boolean traceAll() {// not official!
583 	return Boolean.getBoolean("sun.net.slp.traceALL");
584     }
585 
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 
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 
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 
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 
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 
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
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
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 
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 
850     Vector getConfiguredScopes() {
851 	return (Vector)configuredScopes.clone();
852     }
853 
854     // Return SA scopes.
855 
856     Vector getSAOnlyScopes() {
857 	return (Vector)saOnlyScopes.clone();
858 
859     }
860 
861     // Return the configured scopes for the SA.
862 
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 
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 
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 
939     Vector getPreconfiguredDAs() {
940 	return (Vector)preconfiguredDAs.clone();
941 
942     }
943 
944     // Initialize preconfigured DA list.
945 
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 
1011     boolean getSLPv1NotSupported() {// not official!
1012 	return Boolean.getBoolean("sun.net.slp.SLPv1NotSupported");
1013 
1014     }
1015 
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 
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 
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 
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 
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      */
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 
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 
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 
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      */
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 
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 
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 
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 
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 
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 
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      */
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 
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      */
1537     static long currentSLPTime() {
1538 	return (System.currentTimeMillis() / 1000);
1539     }
1540 
1541     /* security */
1542 
1543     // Indicates whether security class is available.
1544 
1545     boolean getSecurityEnabled() {
1546 	return securityEnabled;
1547 
1548     }
1549 
1550     private static boolean securityEnabled;
1551 
1552     // Indicates whether the securityEnabled property is true
1553 
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 
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 
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 
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
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 
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 
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 
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