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