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 (c) 1999 by Sun Microsystems, Inc.
23  * All rights reserved.
24  *
25  */
26 
27 //  ServiceTable.java: Storage of all services.
28 //  Author:           James Kempf
29 //  Created On:       Fri Oct 10 14:23:25 1997
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Thu Apr  1 10:33:46 1999
32 //  Update Count:     461
33 //
34 
35 package com.sun.slp;
36 
37 import java.util.*;
38 import java.io.*;
39 import java.security.*;
40 import java.net.*;
41 
42 /**
43  * The ServiceTable object records all service registrations. Note
44  * that any exceptions internal to the service table are processed
45  * and either returned as SrvRply objects or are reported.
46  *
47  * @author James Kempf
48  */
49 
50 class ServiceTable extends Object {
51 
52     // Key for SDAAdvert class.
53 
54     static final String SDAADVERT = "com.sun.slp.SDAAdvert";
55 
56     private static final String locationMsg = "Service table";
57 
58     //
59     // Instance variables.
60     //
61 
62     // The service store.
63 
64     protected ServiceStore store = null;
65 
66     //
67     // Class variables.
68 
69     // System properties.
70 
71     static protected SLPConfig conf = null;
72 
73     // Singleton objects for the service tables.
74 
75     static protected ServiceTable table = null;
76 
77     // The ager thread.
78 
79     static protected AgerThread thrAger = null;
80 
81     // Time to sleep. Adjusted depending on incoming URLs.
82 
83     private static long sleepyTime = Defaults.lMaxSleepTime;
84 
85     //
86     // Creation of singleton.
87     //
88 
89     // Protected constructor.
90 
ServiceTable()91     protected ServiceTable() {
92 
93 	if (thrAger != null) {
94 	    return;
95 
96 	}
97 
98 	// Create the ager thread.
99 
100 	thrAger = new AgerThread();
101 
102 	// Set the priority low, so other things (like active discovery)
103 	//  take priority.
104 
105 	thrAger.setPriority(Thread.MIN_PRIORITY);
106 
107 	thrAger.start();
108 
109     }
110 
111     /**
112      * Return an SA service store.
113      *
114      * @return The distinguished table object.
115      */
116 
getServiceTable()117     static ServiceTable getServiceTable()
118 	throws ServiceLocationException {
119 
120 	if (conf == null) {
121 	    conf = SLPConfig.getSLPConfig();
122 
123 	}
124 
125 	if (table == null) {
126 
127 	    table = createServiceTable();
128 
129 	}
130 
131 	return table;
132     }
133 
134     /**
135      * Return a service table object.
136      *
137      * @return The service table object.
138      */
139 
createServiceTable()140     private static ServiceTable createServiceTable()
141 	throws ServiceLocationException {
142 
143 	ServiceTable table = new ServiceTable();
144 
145 	table.store = ServiceStoreFactory.createServiceStore();
146 
147 	return table;
148     }
149 
150     //
151     // Support for serializated registrations.
152     //
153 
154     /**
155      * If any serialized registrations are pending, then unserialize
156      * and register.
157      */
158 
deserializeTable()159     public void deserializeTable() {
160 
161 	// If there are any serialized registrations, then get
162 	//  them and perform registrations.
163 
164 	String serializedURL = conf.getSerializedRegURL();
165 
166 	if (serializedURL != null) {
167 
168 	    ServiceStore serStore = getStoreFromURL(serializedURL);
169 
170 	    if (serStore != null) {
171 		registerStore(serStore);
172 	    }
173 	}
174     }
175 
176     /**
177      * Serialize the table to the URL.
178      *
179      * @param URL String giving the URL to which the store should be
180      * serialized.
181      */
182 
serializeServiceStore(String URL)183     void serializeServiceStore(String URL) {
184 
185 	// Open an object output stream for the URL, serialize through
186 	//  the factory.
187 
188 	try {
189 
190 	    URL url = new URL(URL);
191 	    URLConnection urlConn = url.openConnection();
192 	    OutputStream os = urlConn.getOutputStream();
193 	    BufferedWriter di =
194 		new BufferedWriter(new OutputStreamWriter(os));
195 
196 	    // Serialize the store.
197 
198 	    ServiceStoreFactory.serialize(di, store);
199 
200 	} catch (MalformedURLException ex) {
201 
202 	    conf.writeLog("st_serialized_malform",
203 			  new Object[] {URL});
204 
205 	} catch (UnsupportedEncodingException ex) {
206 
207 	    conf.writeLog("st_unsupported_encoding",
208 			  new Object[] {URL});
209 
210 	} catch (IOException ex) {
211 
212 	    conf.writeLog("st_serialized_ioexception",
213 			  new Object[] {URL, ex});
214 
215 	} catch (ServiceLocationException ex) {
216 
217 	    conf.writeLog("st_serialized_sle",
218 			  new Object[] {URL, ex.getMessage()});
219 
220 	}
221 
222     }
223 
224     // Read proxy registrations from the URL.
225 
getStoreFromURL(String serializedURL)226     private ServiceStore getStoreFromURL(String serializedURL) {
227 
228 	ServiceStore serStore = null;
229 
230 	// Open an object input stream for the URL, deserialize through
231 	//  the factory.
232 
233 	try {
234 
235 	    URL url = new URL(serializedURL);
236 	    InputStream is = url.openStream();
237 	    BufferedReader di = new BufferedReader(new InputStreamReader(is));
238 
239 	    // Deserialize the objects.
240 
241 	    serStore =
242 		ServiceStoreFactory.deserializeServiceStore(di);
243 
244 	} catch (MalformedURLException ex) {
245 
246 	    conf.writeLog("st_serialized_malform",
247 			  new Object[] {serializedURL});
248 
249 	} catch (UnsupportedEncodingException ex) {
250 
251 	    conf.writeLog("st_unsupported_encoding",
252 			  new Object[] {serializedURL});
253 
254 	} catch (IOException ex) {
255 
256 	    conf.writeLog("st_serialized_ioexception",
257 			  new Object[] {
258 		serializedURL,
259 		    ex.getMessage()});
260 
261 	} catch (ServiceLocationException ex) {
262 
263 	    conf.writeLog("st_serialized_sle",
264 			  new Object[] {
265 		serializedURL,
266 		    ex.getMessage()});
267 
268 	}
269 
270 	return serStore;
271     }
272 
273     // Walk the table, performing actual registrations on all records.
274 
registerStore(ServiceStore serStore)275     private void registerStore(ServiceStore serStore) {
276 
277 	// Walk the table.
278 
279 	Enumeration en = serStore.getServiceRecordsByScope(null);
280 	boolean hasURLSig = conf.getHasSecurity();
281 	boolean hasAttrSig = conf.getHasSecurity();
282 	PermSARegTable pregTable = 	SARequester.getPermSARegTable();
283 
284 	while (en.hasMoreElements()) {
285 	    ServiceStore.ServiceRecord rec =
286 		(ServiceStore.ServiceRecord)en.nextElement();
287 	    ServiceURL surl = rec.getServiceURL();
288 	    Vector scopes = rec.getScopes();
289 	    Vector attrs = rec.getAttrList();
290 	    Locale locale = rec.getLocale();
291 	    Hashtable urlSig = null;
292 	    Hashtable attrSig = null;
293 
294 	    // Note that we can't use the Advertiser to register here,
295 	    //  because we may not be listening yet for registrations.
296 	    //  We need to do this all by hand.
297 
298 	    try {
299 
300 		// Create a registration message for refreshing.
301 
302 		CSrvReg creg = new CSrvReg(false,
303 					   locale,
304 					   surl,
305 					   scopes,
306 					   attrs,
307 					   null,
308 					   null);
309 
310 		// We externalize to a server side message if authentication
311 		//  is needed. This creates the auth blocks for the scopes.
312 		//  Doing this in any other way is alot more complicated,
313 		//  although doing it this way seems kludgy.
314 
315 		if (hasURLSig || hasAttrSig) {
316 		    ByteArrayOutputStream baos = new ByteArrayOutputStream();
317 
318 		    creg.getHeader().externalize(baos, false, true);
319 
320 		    ByteArrayInputStream bais =
321 			new ByteArrayInputStream(baos.toByteArray());
322 		    bais.read();	// pop off version and function code...
323 		    bais.read();
324 		    DataInputStream dis = new DataInputStream(bais);
325 		    SLPHeaderV2 hdr = new SLPHeaderV2();
326 		    hdr.parseHeader(SrvLocHeader.SrvReg, dis);
327 		    SSrvReg sreg = new SSrvReg(hdr, dis);
328 
329 		    // Now we've got it, after much effort. Get the auths.
330 
331 		    urlSig = sreg.URLSignature;
332 		    attrSig = sreg.attrSignature;
333 
334 		}
335 
336 		store.register(surl, attrs, scopes, locale, urlSig, attrSig);
337 
338 		// Now we've got to put the registration into the
339 		//  PermSARegTable. Again, we do everything by hand
340 		//  because we can't use Advertiser.
341 
342 		if (surl.getIsPermanent()) {
343 		    pregTable.reg(surl, creg);
344 
345 		}
346 
347 		// Report registration.
348 
349 		if (conf.regTest()) {
350 		    conf.writeLog("st_reg_add",
351 				  new Object[] {
352 			locationMsg,
353 			    locale,
354 			    surl.getServiceType(),
355 			    surl,
356 			    attrs,
357 			    scopes});
358 
359 		}
360 	    } catch (ServiceLocationException ex) {
361 
362 		String msg = ex.getMessage();
363 
364 		conf.writeLog("st_serialized_seex",
365 			      new Object[] {
366 		    new Integer(ex.getErrorCode()),
367 			surl,
368 			(msg == null ? "<no message>":msg)});
369 
370 	    } catch (Exception ex) {
371 
372 		String msg = ex.getMessage();
373 
374 		conf.writeLog("st_serialized_seex",
375 			      new Object[] {
376 		    surl,
377 			(msg == null ? "<no message>":msg)});
378 	    }
379 	}
380     }
381 
382     //
383     // Record aging.
384     //
385 
386     //
387     // Run the thread that ages out records.
388     //
389 
390     private class AgerThread extends Thread {
391 
run()392 	public void run() {
393 
394 	    setName("SLP Service Table Age-out");
395 	    long alarmTime = sleepyTime;  // when to wake up next
396 	    long wentToSleep = 0;	    // what time we went to bed
397 
398 	    while (true) {
399 
400 		try {
401 
402 		    // Record when we went to sleep.
403 
404 		    wentToSleep = System.currentTimeMillis();
405 
406 		    // Sleep for the minimum amount of time needed before we
407 		    //  must wake up and check.
408 
409 		    sleep(alarmTime);
410 
411 		} catch (InterruptedException ie) {
412 
413 		    // A new registration came in. Calculate how much time
414 		    //  remains until we would have woken up. If this is
415 		    //  less than the new sleepyTime, then we set the alarm
416 		    //  for this time. If it is more, then we set the alarm
417 		    //  for the new sleepyTime.
418 
419 		    long remainingSleepTime =
420 			(wentToSleep + alarmTime) - System.currentTimeMillis();
421 
422 		    remainingSleepTime =		// just in case...
423 			((remainingSleepTime <= 0) ? 0 : remainingSleepTime);
424 
425 		    alarmTime = sleepyTime;
426 
427 		    if (remainingSleepTime < alarmTime) {
428 			alarmTime = remainingSleepTime;
429 
430 		    }
431 
432 		    continue;  // we don't have to walk yet...
433 
434 		}
435 
436 		// Walk the table, get the new alarm and sleepy times.
437 
438 		if (table != null) {
439 		    table.ageStore();
440 
441 		    alarmTime = sleepyTime;
442 
443 		}
444 	    }
445 	}
446 
447     }
448 
449     /**
450      * Age the service store.
451      */
452 
453     // this method cannot be private... due to compiler weakness
ageStore()454     void ageStore() {
455 
456 	try {
457 
458 	    // We synchronize in case somebody registers and tries to
459 	    //  change sleepy time.
460 
461 	    synchronized (store) {
462 		Vector deleted = new Vector();
463 
464 		sleepyTime = store.ageOut(deleted);
465 
466 		// Track unregistered services.
467 
468 		int i, n = deleted.size();
469 
470 		for (i = 0; i < n; i++) {
471 		    ServiceStore.ServiceRecord rec =
472 			(ServiceStore.ServiceRecord)deleted.elementAt(i);
473 		    ServiceURL surl = rec.getServiceURL();
474 
475 		    trackRegisteredServiceTypes(); // it's deleted...
476 
477 		}
478 
479 	    }
480 
481 	} catch (RuntimeException ex) {
482 
483 	    reportNonfatalException(ex, new Vector(), store);
484 
485 	} catch (ServiceLocationException ex) {
486 
487 	    reportNonfatalException(ex, new Vector(), store);
488 
489 	}
490 
491     }
492 
493     //
494     // SLP Service Table operations (register, deregister, etc.)
495     //
496 
497     /**
498      * Process the registration and record if no errors found.
499      *
500      * @param req Service registration request message.
501      * @return SrvLocMsg A service registration acknowledgement.
502      */
503 
register(SSrvReg req)504     SrvLocMsg register(SSrvReg req) {
505 
506 	SrvLocHeader hdr = req.getHeader();
507 	Locale locale = hdr.locale;
508 	boolean fresh = hdr.fresh;
509 	Vector scopes = hdr.scopes;
510 	ServiceURL surl = req.URL;
511 	String serviceType = req.serviceType;
512 	Vector attrList = req.attrList;
513 	Hashtable urlSig = req.URLSignature;
514 	Hashtable attrSig = req.attrSignature;
515 	short errorCode =
516 	    (fresh ? ServiceLocationException.INVALID_REGISTRATION :
517 	    ServiceLocationException.INVALID_UPDATE);
518 
519 	try {
520 
521 	    // If a sig block came in, verify it.
522 
523 	    if (urlSig != null) {
524 
525 		AuthBlock.verifyAll(urlSig);
526 	    }
527 
528 	    if (attrSig != null) {
529 
530 		AuthBlock.verifyAll(attrSig);
531 
532 	    }
533 
534 	    // Check whether the URL has a zero lifetime. If so, it
535 	    // isn't cached.
536 
537 	    if (surl.getLifetime() <= 0) {
538 		throw
539 		    new ServiceLocationException(errorCode,
540 						 "st_zero",
541 						 new Object[0]);
542 
543 	    }
544 
545 	    // Check if the service type is restricted. If so, nobody outside
546 	    //  this process is allowed to register it.
547 
548 	    checkForRestrictedType(surl.getServiceType());
549 
550 	    // Check that attribute signature bit on implies URL signature
551 	    //  bit on.
552 
553 	    if (attrSig != null && urlSig == null) {
554 		throw
555 		    new ServiceLocationException(errorCode,
556 						 "st_attr_sig",
557 						 new Object[0]);
558 
559 	    }
560 
561 	    // If a signature and the fresh bit was not set, error since signed
562 	    //  registrations don't allow updating.
563 
564 	    if (urlSig != null && !fresh) {
565 		throw
566 		    new ServiceLocationException(
567 				ServiceLocationException.INVALID_UPDATE,
568 				"st_prot_update",
569 				new Object[0]);
570 	    }
571 
572 	    // Check if scopes are supported.
573 
574 	    if (!areSupportedScopes(scopes)) {
575 		throw
576 		    new ServiceLocationException(
577 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
578 				"st_scope_unsup",
579 				new Object[0]);
580 	    }
581 
582 	    // Check if the reg is signed and auth is off or vice versa.
583 	    //  Check is really simple. If security is on, then all regs
584 	    //  to this DA/SA server must be signed, so toss out any regs
585 	    //  that aren't, and vice versa.
586 
587 	    if (conf.getHasSecurity() && (urlSig == null || attrSig == null)) {
588 		throw
589 		    new ServiceLocationException(
590 				ServiceLocationException.AUTHENTICATION_FAILED,
591 				"st_unprot_non_reg",
592 				new Object[0]);
593 
594 	    } else if (!conf.getHasSecurity() &&
595 		       (urlSig != null || attrSig != null)) {
596 		throw
597 		    new ServiceLocationException(
598 				ServiceLocationException.INVALID_REGISTRATION,
599 				"st_prot_non_reg",
600 				new Object[0]);
601 
602 	    }
603 
604 	    // Merge any duplicates.
605 
606 	    Vector attrs = new Vector();
607 	    Hashtable attrHash = new Hashtable();
608 	    int i, n = attrList.size();
609 
610 	    for (i = 0; i < n; i++) {
611 		ServiceLocationAttribute attr =
612 		    (ServiceLocationAttribute)attrList.elementAt(i);
613 
614 		ServiceLocationAttribute.mergeDuplicateAttributes(
615 								  attr,
616 								  attrHash,
617 								  attrs,
618 								  false);
619 	    }
620 
621 	    // Store register or update.
622 
623 	    boolean existing = false;
624 
625 	    if (fresh) {
626 		existing = store.register(surl,
627 					  attrs,
628 					  scopes,
629 					  locale,
630 					  urlSig,
631 					  attrSig);
632 
633 		// Track registred service types in case we get a
634 		// SAAdvert solicatation.
635 
636 		trackRegisteredServiceTypes();
637 
638 	    } else {
639 		store.updateRegistration(surl, attrs, scopes, locale);
640 
641 	    }
642 
643 	    // Create the reply.
644 
645 	    SrvLocMsg ack = req.makeReply(existing);
646 
647 	    if (conf.regTest()) {
648 		conf.writeLog((fresh ? "st_reg_add":"st_reg_update"),
649 			      new Object[] {
650 		    locationMsg,
651 			locale,
652 			serviceType,
653 			surl,
654 			attrs,
655 			scopes});
656 
657 	    }
658 
659 	    if (conf.traceAll()) {
660 		conf.writeLog("st_dump", new Object[] {locationMsg});
661 		store.dumpServiceStore();
662 
663 	    }
664 
665 	    // Calculate time increment until next update. This is used
666 	    //  to adjust the sleep interval in the ager thread.
667 
668 	    long sTime = getSleepIncrement(surl);
669 
670 	    // We synchronize in case the ager thread is in the middle
671 	    //  of trying to set the time.
672 
673 	    synchronized (store) {
674 
675 		// If we need to wake up sooner, adjust the sleep time.
676 
677 		if (sTime < sleepyTime) {
678 
679 		    sleepyTime = sTime;
680 
681 		    // Interrupt the thread so we go back to
682 		    //  sleep for the right amount of time.
683 
684 		    thrAger.interrupt();
685 		}
686 	    }
687 
688 	    return ack;
689 
690 	} catch (ServiceLocationException ex) {
691 
692 	    if (conf.traceDrop()) {
693 		conf.writeLog("st_reg_drop",
694 			      new Object[] {
695 		    locationMsg,
696 			ex.getMessage()+"("+ex.getErrorCode()+")",
697 			locale,
698 			serviceType,
699 			surl,
700 			attrList,
701 			scopes});
702 	    }
703 
704 	    return hdr.makeErrorReply(ex);
705 
706 	} catch (RuntimeException ex) {
707 
708 	    // These exceptions are not declared in throws but can occur
709 	    //  anywhere.
710 
711 	    Vector args = new Vector();
712 
713 	    args.addElement(req);
714 
715 	    reportNonfatalException(ex, args, store);
716 
717 	    return hdr.makeErrorReply(ex);
718 
719 	}
720     }
721 
722     /**
723      * Process the deregistration and return the result in a reply.
724      *
725      * @param req Service deregistration request message.
726      * @return SrvLocMsg A service registration acknowledgement.
727      */
728 
deregister(SSrvDereg req)729     SrvLocMsg deregister(SSrvDereg req) {
730 
731 	// We need to determine whether this is an attribute deregistration
732 	//  or a deregistration of the entire URL.
733 
734 	SrvLocHeader hdr = req.getHeader();
735 	Locale locale = hdr.locale;
736 	Vector scopes = hdr.scopes;
737 	ServiceURL surl = req.URL;
738 	Hashtable urlSig = req.URLSignature;
739 	Vector tags = req.tags;
740 	short errorCode = ServiceLocationException.OK;
741 
742 	try {
743 
744 	    // Verify if signature is nonnull.
745 
746 	    if (urlSig != null) {
747 		AuthBlock.verifyAll(urlSig);
748 
749 	    }
750 
751 	    // Check if the service type is restricted. If so, nobody outside
752 	    //  this process is allowed to register it.
753 
754 	    checkForRestrictedType(surl.getServiceType());
755 
756 	    // Error if there's a signature and attempt at deleting attributes.
757 
758 	    if ((urlSig != null) && (tags != null)) {
759 		throw
760 		    new ServiceLocationException(
761 				ServiceLocationException.AUTHENTICATION_FAILED,
762 				"st_prot_attr_dereg",
763 				new Object[0]);
764 	    }
765 
766 	    // Check if scope is protected and auth is off or vice versa.
767 	    //  Check is really simple. If security is on, then all scopes
768 	    //  in this DA/SA server are protected, so toss out any regs
769 	    //  that aren't, and vice versa.
770 
771 	    if (conf.getHasSecurity() && urlSig == null) {
772 		throw
773 		    new ServiceLocationException(
774 				ServiceLocationException.AUTHENTICATION_FAILED,
775 				"st_unprot_non_dereg",
776 				new Object[0]);
777 
778 	    } else if (!conf.getHasSecurity() && urlSig != null) {
779 		throw
780 		    new ServiceLocationException(
781 				ServiceLocationException.INVALID_REGISTRATION,
782 				"st_prot_non_dereg",
783 				new Object[0]);
784 
785 	    }
786 
787 	    // If it's a service URL, then deregister the URL.
788 
789 	    if (tags == null) {
790 		store.deregister(surl, scopes, urlSig);
791 
792 		// Track registred service types in case we get a
793 		// SAAdvert solicatation.
794 
795 		trackRegisteredServiceTypes();
796 
797 	    } else {
798 
799 		// Just delete the attributes.
800 
801 		store.deleteAttributes(surl, scopes, tags, locale);
802 
803 	    }
804 
805 	    // Create the reply.
806 
807 	    SrvLocMsg ack = req.makeReply();
808 
809 	    if (conf.regTest()) {
810 		conf.writeLog((tags == null ? "st_dereg":"st_delattr"),
811 			      new Object[] {
812 		    locationMsg,
813 	                locale,
814 			surl.getServiceType(),
815 			surl,
816 			tags});
817 
818 	    }
819 
820 	    if (conf.traceAll()) {
821 		conf.writeLog("st_dump",
822 			      new Object[] {locationMsg});
823 		store.dumpServiceStore();
824 
825 	    }
826 
827 	    return ack;
828 
829 	} catch (ServiceLocationException ex) {
830 
831 	    if (conf.traceDrop()) {
832 		conf.writeLog((tags == null ?
833 			       "st_dereg_drop" : "st_dereg_attr_drop"),
834 			      new Object[] {
835 		    locationMsg,
836 			ex.getMessage()+"("+ex.getErrorCode()+")",
837 			locale,
838 			surl.getServiceType(),
839 			surl,
840 			tags});
841 	    }
842 
843 	    return hdr.makeErrorReply(ex);
844 
845 	} catch (RuntimeException ex) {
846 
847 	    // These exceptions are not declared in throws but can occur
848 	    //  anywhere.
849 
850 	    Vector args = new Vector();
851 
852 	    args.addElement(req);
853 
854 	    reportNonfatalException(ex, args, store);
855 
856 	    return hdr.makeErrorReply(ex);
857 
858 	}
859     }
860 
861     /**
862      * Process the service type request and return the result in a reply.
863      *
864      * @param req Service type request message.
865      * @return SrvTypeRply A service type reply.
866      */
867 
findServiceTypes(SSrvTypeMsg req)868     SrvLocMsg findServiceTypes(SSrvTypeMsg req) {
869 
870 	SrvLocHeader hdr = req.getHeader();
871 	Vector scopes = hdr.scopes;
872 	String namingAuthority = req.namingAuthority;
873 	short errorCode = ServiceLocationException.OK;
874 
875 	try {
876 
877 	    // Check whether the scope is supported.
878 
879 	    if (!areSupportedScopes(scopes)) {
880 		throw
881 		    new ServiceLocationException(
882 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
883 				"st_scope_unsup",
884 				new Object[0]);
885 
886 	    }
887 
888 	    // Get the vector of service types in the store, independent
889 	    //  of language.
890 
891 	    Vector types = store.findServiceTypes(namingAuthority, scopes);
892 
893 	    // Create the reply.
894 
895 	    SrvLocMsg ack = req.makeReply(types);
896 
897 	    if (conf.traceAll()) {
898 		conf.writeLog("st_stypes",
899 			      new Object[] {
900 		    locationMsg,
901 			namingAuthority,
902 			scopes,
903 			types});
904 	    }
905 
906 	    return ack;
907 
908 	} catch (ServiceLocationException ex) {
909 
910 	    if (conf.traceDrop()) {
911 		conf.writeLog("st_stypes_drop",
912 			      new Object[] {
913 		    locationMsg,
914 			ex.getMessage()+"("+ex.getErrorCode()+")",
915 			namingAuthority,
916 			scopes,
917 			hdr.locale});
918 	    }
919 
920 	    return hdr.makeErrorReply(ex);
921 
922 	} catch (RuntimeException ex) {
923 
924 	    // These exceptions are not declared in throws but can occur
925 	    //  anywhere.
926 
927 	    Vector args = new Vector();
928 
929 	    args.addElement(req);
930 
931 	    reportNonfatalException(ex, args, store);
932 
933 	    return hdr.makeErrorReply(ex);
934 
935 	}
936     }
937 
938     /**
939      * Process the service request and return the result in a reply.
940      *
941      * @param req Service request message.
942      * @return SrvRply A service reply.
943      */
944 
findServices(SSrvMsg req)945     SrvLocMsg findServices(SSrvMsg req) {
946 
947 	SrvLocHeader hdr = req.getHeader();
948 	Locale locale = hdr.locale;
949 	Vector scopes = hdr.scopes;
950 	String serviceType = req.serviceType;
951 	String query = req.query;
952 	short errorCode = ServiceLocationException.OK;
953 
954 	try {
955 
956 	    // Check whether the scope is supported.
957 
958 	    if (!areSupportedScopes(scopes)) {
959 		throw
960 		    new ServiceLocationException(
961 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
962 				"st_scope_unsup",
963 				new Object[0]);
964 	    }
965 
966 	    // Get the hashtable of returns.
967 
968 	    Hashtable returns =
969 		store.findServices(serviceType,
970 				   scopes,
971 				   query,
972 				   locale);
973 
974 	    // Get the hashtable of services v.s. scopes, and signatures, if
975 	    //  any.
976 
977 	    Hashtable services =
978 		(Hashtable)returns.get(ServiceStore.FS_SERVICES);
979 	    Hashtable signatures =
980 		(Hashtable)returns.get(ServiceStore.FS_SIGTABLE);
981 	    boolean hasSignatures = (signatures != null);
982 
983 	    // for each candidate URL, make sure it has the requested SPI
984 	    // (if any)
985 	    if (hasSignatures && !req.spi.equals("")) {
986 		Enumeration allSurls = services.keys();
987 		while (allSurls.hasMoreElements()) {
988 		    Object aSurl = allSurls.nextElement();
989 		    Hashtable auths = (Hashtable) signatures.get(aSurl);
990 		    AuthBlock auth =
991 			AuthBlock.getEquivalentAuth(req.spi, auths);
992 		    if (auth == null) {
993 			// doesn't have the requested SPI
994 			services.remove(aSurl);
995 		    }
996 		}
997 	    }
998 
999 	    // Create return message.
1000 
1001 	    SrvLocMsg ack = req.makeReply(services, signatures);
1002 
1003 	    if (conf.traceAll()) {
1004 		conf.writeLog("st_sreq",
1005 			      new Object[] {
1006 		    locationMsg,
1007 			serviceType,
1008 			scopes,
1009 			query,
1010 			locale,
1011 			services,
1012 			signatures});
1013 	    }
1014 
1015 	    return ack;
1016 
1017 	} catch (ServiceLocationException ex) {
1018 
1019 	    if (conf.traceDrop()) {
1020 		conf.writeLog("st_sreq_drop",
1021 			      new Object[] {
1022 		    locationMsg,
1023 			ex.getMessage()+"("+ex.getErrorCode()+")",
1024 			serviceType,
1025 			scopes,
1026 			query,
1027 			locale});
1028 	    }
1029 
1030 	    return hdr.makeErrorReply(ex);
1031 
1032 	} catch (RuntimeException ex) {
1033 
1034 	    // These exceptions are not declared in throws but can occur
1035 	    //  anywhere.
1036 
1037 	    Vector args = new Vector();
1038 
1039 	    args.addElement(req);
1040 
1041 	    reportNonfatalException(ex, args, store);
1042 
1043 	    return hdr.makeErrorReply(ex);
1044 
1045 	}
1046     }
1047 
1048     /**
1049      * Process the attribute request and return the result in a reply.
1050      *
1051      * @param req Attribute request message.
1052      * @return AttrRply An attribute reply.
1053      */
1054 
findAttributes(SAttrMsg req)1055     SrvLocMsg findAttributes(SAttrMsg req) {
1056 
1057 	// We need to determine whether this is a request for attributes
1058 	//  on a specific URL or for an entire service type.
1059 
1060 	SrvLocHeader hdr = req.getHeader();
1061 	Vector scopes = hdr.scopes;
1062 	Locale locale = hdr.locale;
1063 	ServiceURL surl = req.URL;
1064 	String serviceType = req.serviceType;
1065 	Vector tags = req.tags;
1066 	short errorCode = ServiceLocationException.OK;
1067 
1068 	try {
1069 
1070 	    // Check whether the scope is supported.
1071 
1072 	    if (!areSupportedScopes(scopes)) {
1073 	throw
1074 	    new ServiceLocationException(
1075 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
1076 				"st_scope_unsup",
1077 				new Object[0]);
1078 	    }
1079 
1080 	    Vector attributes = null;
1081 	    Hashtable sig = null;
1082 
1083 	    // If it's a service URL, then get the attributes just for
1084 	    // that URL.
1085 
1086 	    if (serviceType == null) {
1087 
1088 		// If the attrs are signed, then error if any tags, since
1089 		//  we must ask for *all* attributes in for a signed reg
1090 
1091 		if (!req.spi.equals("") && tags.size() > 0) {
1092 		    throw
1093 			new ServiceLocationException(
1094 				ServiceLocationException.AUTHENTICATION_FAILED,
1095 				"st_par_attr",
1096 				new Object[0]);
1097 
1098 		}
1099 
1100 		Hashtable ht =
1101 		    store.findAttributes(surl, scopes, tags, locale);
1102 
1103 		// Get the attributes and signatures.
1104 
1105 		attributes = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
1106 
1107 		sig = (Hashtable)ht.get(ServiceStore.FA_SIG);
1108 
1109 		// make sure the attr has the requested SPI (if any)
1110 		if (sig != null && !req.spi.equals("")) {
1111 		    AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, sig);
1112 		    if (auth == null) {
1113 			// return empty
1114 			attributes = new Vector();
1115 		    }
1116 		}
1117 
1118 	    } else {
1119 
1120 		if (!req.spi.equals("")) {
1121 		    throw
1122 			new ServiceLocationException(
1123 				ServiceLocationException.AUTHENTICATION_FAILED,
1124 				"st_par_attr",
1125 				new Object[0]);
1126 		}
1127 
1128 		// Otherwise find the attributes for all service types.
1129 
1130 		attributes =
1131 		    store.findAttributes(serviceType, scopes, tags, locale);
1132 
1133 	    }
1134 
1135 	    ServiceType type =
1136 		(serviceType == null ? surl.getServiceType():
1137 		 new ServiceType(serviceType));
1138 
1139 
1140 	    // Create the reply.
1141 
1142 	    SrvLocMsg ack = req.makeReply(attributes, sig);
1143 
1144 	    if (conf.traceAll()) {
1145 		conf.writeLog((serviceType != null ?
1146 			       "st_st_attr" : "st_url_attr"),
1147 			      new Object[] {
1148 		    locationMsg,
1149 			(serviceType != null ? serviceType.toString() :
1150 			 surl.toString()),
1151 		      	scopes,
1152 			tags,
1153 			locale,
1154 			attributes});
1155 	    }
1156 
1157 	    return ack;
1158 
1159 	} catch (ServiceLocationException ex) {
1160 
1161 	    if (conf.traceDrop()) {
1162 		conf.writeLog((serviceType != null ? "st_st_attr_drop":
1163 			       "st_url_attr_drop"),
1164 			      new Object[] {
1165 		    locationMsg,
1166 			ex.getMessage()+"("+ex.getErrorCode()+")",
1167 		        (serviceType != null ? serviceType.toString() :
1168 			 surl.toString()),
1169 		        scopes,
1170 			tags,
1171 			locale});
1172 	    }
1173 
1174 	    return hdr.makeErrorReply(ex);
1175 
1176 	} catch (RuntimeException ex) {
1177 
1178 	    // These exceptions are not declared in throws but can occur
1179 	    //  anywhere.
1180 
1181 	    Vector args = new Vector();
1182 
1183 	    args.addElement(req);
1184 
1185 	    reportNonfatalException(ex, args, store);
1186 
1187 	    return hdr.makeErrorReply(ex);
1188 
1189 	}
1190     }
1191 
1192     // Return the service record corresponding to the URL.
1193 
getServiceRecord(ServiceURL URL, Locale locale)1194     ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL,
1195 						Locale locale) {
1196 	return store.getServiceRecord(URL, locale);
1197 
1198     }
1199 
1200     //
1201     // Utility methods.
1202     //
1203 
1204     //
1205     //  Protected/private methods.
1206     //
1207 
1208     // Check whether the type is restricted, through an exception if so.
1209 
checkForRestrictedType(ServiceType type)1210     private void checkForRestrictedType(ServiceType type)
1211 	throws ServiceLocationException {
1212 
1213 	if (Defaults.restrictedTypes.contains(type)) {
1214 	    throw
1215 		new ServiceLocationException(
1216 				ServiceLocationException.INVALID_REGISTRATION,
1217 				"st_restricted_type",
1218 				new Object[] {type});
1219 	}
1220     }
1221 
1222     // Insert a record for type "service-agent" with attributes having
1223     //  the types currently supported, if the new URL is not on the
1224     //  list of supported types. This allows us to perform queries
1225     //  for supported service types.
1226 
trackRegisteredServiceTypes()1227     private void trackRegisteredServiceTypes()
1228 	throws ServiceLocationException {
1229 
1230 	// First find the types.
1231 
1232 	Vector types = store.findServiceTypes(Defaults.ALL_AUTHORITIES,
1233 					      conf.getSAConfiguredScopes());
1234 
1235 	// Get preconfigured attributes.
1236 
1237 	Vector attrs = conf.getSAAttributes();
1238 
1239 	// Make an attribute with the service types.
1240 
1241 	ServiceLocationAttribute attr =
1242 	    new ServiceLocationAttribute(Defaults.SERVICE_TYPE_ATTR_ID,
1243 					 types);
1244 
1245 	attrs.addElement(attr);
1246 
1247 	// Construct URL to use on all interfaces.
1248 
1249 	Vector interfaces = conf.getInterfaces();
1250 	int i, n = interfaces.size();
1251 
1252 	for (i = 0; i < n; i++) {
1253 	    InetAddress addr = (InetAddress)interfaces.elementAt(i);
1254 	    ServiceURL url =
1255 		new ServiceURL(Defaults.SUN_SA_SERVICE_TYPE + "://" +
1256 			       addr.getHostAddress(),
1257 			       ServiceURL.LIFETIME_MAXIMUM);
1258 
1259 	    Vector scopes = conf.getSAOnlyScopes();
1260 
1261 	    Locale locale = Defaults.locale;
1262 
1263 	    // Make a new registration for this SA.
1264 
1265 	    store.register(url,
1266 			   attrs,
1267 			   scopes,
1268 			   locale,
1269 			   null,
1270 			   null);  // we could sign, but we do that later...
1271 	}
1272 
1273 	// Note that we don't need a refresh on the URLs because they
1274 	//  will get refreshed when the service URLs that they track
1275 	//  are refreshed. If the tracked URLs aren't refreshed, then
1276 	//  these will get updated when the tracked URLs age out.
1277     }
1278 
1279     // Return true if the scopes in the vector are supported by the DA
1280     //  or SA server.
1281 
areSupportedScopes(Vector scopes)1282     final private boolean areSupportedScopes(Vector scopes) {
1283 
1284 	Vector configuredScopes = conf.getSAConfiguredScopes();
1285 	Vector saOnlyScopes = conf.getSAOnlyScopes();
1286 	int i = 0;
1287 
1288 	while (i < scopes.size()) {
1289 	    Object o = scopes.elementAt(i);
1290 
1291 	    // Remove it if we don't support it.
1292 
1293 	    if (!configuredScopes.contains(o) && !saOnlyScopes.contains(o)) {
1294 		// This will shift the Vector's elements down one, so
1295 		// don't increment i
1296 		scopes.removeElementAt(i);
1297 	    } else {
1298 		i++;
1299 	    }
1300 	}
1301 
1302 	if (scopes.size() <= 0) {
1303 	    return false;
1304 
1305 	}
1306 
1307 	return true;
1308     }
1309 
1310     /**
1311      * Return the sleep increment from the URL lifetime. Used by the
1312      * ServiceStore to calculate the new sleep interval in addition
1313      * to this class, when a new URL comes in. The algorithm
1314      * subtracts x% of the lifetime from the lifetime and schedules the
1315      * timeout at that time.
1316      *
1317      * @param url The URL to use for calculation.
1318      * @return The sleep interval.
1319      */
1320 
getSleepIncrement(ServiceURL url)1321     private long getSleepIncrement(ServiceURL url) {
1322 	long urlLifetime = (long)(url.getLifetime() * 1000);
1323 	long increment =
1324 	    (long)((float)urlLifetime * Defaults.fRefreshGranularity);
1325 	long sTime = urlLifetime - increment;
1326 
1327 	// If URL lives only one second, update every half second.
1328 
1329 	if (sTime <= 0) {
1330 	    sTime = 500;
1331 
1332 	}
1333 
1334 	return sTime;
1335     }
1336 
1337     // Make a DAADvert for the DA service request. This only applies
1338     //  to DAs, not to SA servers.
1339 
1340     SrvLocMsg
makeDAAdvert(SSrvMsg rqst, InetAddress daAddr, SLPConfig conf)1341 	makeDAAdvert(SSrvMsg rqst,
1342 		     InetAddress daAddr,
1343 		     SLPConfig conf) {
1344 
1345 	SrvLocHeader hdr = rqst.getHeader();
1346 	Vector scopes = hdr.scopes;
1347 	short xid = hdr.xid;
1348 	String query = rqst.query;
1349 
1350 	try {
1351 
1352 	    // If security is on, proceed only if we can sign as rqst.spi
1353 	    if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
1354 		throw new ServiceLocationException(
1355 			ServiceLocationException.AUTHENTICATION_UNKNOWN,
1356 			"st_cant_sign_as",
1357 			new Object[] {rqst.spi});
1358 	    }
1359 
1360 	    // Get the hashtable of service URLs v.s. scopes.
1361 
1362 	    Hashtable services =
1363 		ServerDATable.getServerDATable().returnMatchingDAs(query);
1364 
1365 	    // Go through the table checking whether the IP address came back.
1366 
1367 	    Enumeration urls = services.keys();
1368 	    boolean foundIt = false;
1369 	    String strDAAddr = daAddr.getHostAddress();
1370 
1371 	    while (urls.hasMoreElements()) {
1372 		ServiceURL url = (ServiceURL)urls.nextElement();
1373 
1374 		if (url.getHost().equals(strDAAddr)) {
1375 		    foundIt = true;
1376 		    break;
1377 
1378 		}
1379 	    }
1380 
1381 	    // If we didn't find anything, make a null service reply.
1382 
1383 	    if (!foundIt) {
1384 		return rqst.makeReply(new Hashtable(), new Hashtable());
1385 
1386 	    }
1387 
1388 	    return makeDAAdvert(hdr, daAddr, xid, scopes, conf);
1389 
1390 
1391 	} catch (ServiceLocationException ex) {
1392 
1393 	    return hdr.makeErrorReply(ex);
1394 
1395 	}
1396 
1397     }
1398 
1399     // Make a DAAdvert from the input arguments.
1400     SrvLocMsg
makeDAAdvert(SrvLocHeader hdr, InetAddress daAddr, short xid, Vector scopes, SLPConfig config)1401 	makeDAAdvert(SrvLocHeader hdr,
1402 		     InetAddress daAddr,
1403 		     short xid,
1404 		     Vector scopes,
1405 		     SLPConfig config)
1406 	throws ServiceLocationException {
1407 
1408 	// If this is a request for a V1 Advert, truncate the scopes vector
1409 	//  since DA solicitations in V1 are always unscoped
1410 
1411 	if (hdr.version == 1) {
1412 	    scopes = new Vector();
1413 
1414 	}
1415 
1416 	// Check if we support scopes first. If not, return an
1417 	//  error reply unless the scope vector is zero. Upper layers
1418 	//  must sort out whether this is a unicast or multicast.
1419 
1420 	if (scopes.size() > 0 && !areSupportedScopes(scopes)) {
1421 	    throw
1422 		new ServiceLocationException(
1423 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
1424 				"st_scope_unsup",
1425 				new Object[0]);
1426 
1427 	}
1428 
1429 	// Get the service store's timestamp. This must be the
1430 	//  time since last stateless reboot for a stateful store,
1431 	//  or the current time.
1432 
1433 	long timestamp = store.getStateTimestamp();
1434 
1435 	ServiceURL url =
1436 	    new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" +
1437 			   daAddr.getHostAddress(),
1438 			   ServiceURL.LIFETIME_DEFAULT);
1439 
1440 	SDAAdvert advert =
1441 	    hdr.getDAAdvert(xid,
1442 			    timestamp,
1443 			    url,
1444 			    scopes,
1445 			    conf.getDAAttributes());
1446 
1447 	return advert;
1448     }
1449 
1450     // Make a SAADvert for the SA service request. This only applies
1451     //  to SA servers, not DA's. Note that we only advertise the "public"
1452     //  scopes, not the private ones.
1453 
1454     SSAAdvert
makeSAAdvert(SSrvMsg rqst, InetAddress interfac, SLPConfig conf)1455 	makeSAAdvert(SSrvMsg rqst,
1456 		     InetAddress interfac,
1457 		     SLPConfig conf)
1458 	throws ServiceLocationException {
1459 	SrvLocHeader hdr = rqst.getHeader();
1460 	int version = hdr.version;
1461 	short xid = hdr.xid;
1462 	Locale locale = hdr.locale;
1463 	Vector scopes = hdr.scopes;
1464 	String query = rqst.query;
1465 	String serviceType = rqst.serviceType;
1466 	Vector saOnlyScopes = conf.getSAOnlyScopes();
1467 
1468 	// If security is on, proceed only if we can sign as rqst.spi
1469 	if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
1470 	    throw new ServiceLocationException(
1471 			ServiceLocationException.AUTHENTICATION_UNKNOWN,
1472 			"st_cant_sign_as",
1473 			new Object[] {rqst.spi});
1474 	}
1475 
1476 
1477 	// Check if we support scopes first. Note that this may allow
1478 	//  someone to get at the SA only scopes off machine, but that's
1479 	//  OK. Since the SAAdvert is only ever multicast, this is OK.
1480 
1481 	if (!areSupportedScopes(scopes) && !(scopes.size() <= 0)) {
1482 	    return null;
1483 
1484 	}
1485 
1486 	// If the scopes vector is null, then use all configured scopes.
1487 
1488 	if (scopes.size() <= 0) {
1489 	    scopes = (Vector)conf.getSAConfiguredScopes().clone();
1490 
1491 	}
1492 
1493 	// Check to be sure the query matches.
1494 	//  If it doesn't, we don't need to return anything.
1495 
1496 	Hashtable returns =
1497 	    store.findServices(Defaults.SUN_SA_SERVICE_TYPE.toString(),
1498 			       saOnlyScopes,
1499 			       query,
1500 			       Defaults.locale);
1501 	Hashtable services =
1502 	    (Hashtable)returns.get(ServiceStore.FS_SERVICES);
1503 	Enumeration en = services.keys();
1504 
1505 	// Indicates we don't support the service type.
1506 
1507 	if (!en.hasMoreElements()) {
1508 	    return null;
1509 
1510 	}
1511 
1512 	// Find the URL to use. The interface on which the message came in
1513 	//  needs to match one of the registered URLs.
1514 
1515 	ServiceURL url = null;
1516 	ServiceURL surl = null;
1517 	String addr = interfac.getHostAddress();
1518 
1519 	while (en.hasMoreElements()) {
1520 	    surl = (ServiceURL)en.nextElement();
1521 
1522 	    if (addr.equals(surl.getHost())) {
1523 		url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" +
1524 				     addr,
1525 				     ServiceURL.LIFETIME_DEFAULT);
1526 		break;
1527 	    }
1528 	}
1529 
1530 	// If none of the URLs matched this interface, then return null.
1531 
1532 	if (url == null) {
1533 	    return null;
1534 
1535 	}
1536 
1537 	// Find the SA's attributes.
1538 
1539 	Hashtable ht =
1540 	    store.findAttributes(surl,
1541 				 saOnlyScopes,
1542 				 new Vector(),
1543 				 Defaults.locale);
1544 
1545 	Vector attrs = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
1546 
1547 	// Construct return.
1548 
1549 	return
1550 	    new SSAAdvert(version,
1551 			  xid,
1552 			  locale,
1553 			  url,
1554 			  conf.getSAConfiguredScopes(), // report all scopes...
1555 			  attrs);
1556     }
1557 
1558     /**
1559      * Report a fatal exception to the log.
1560      *
1561      * @param ex The exception to report.
1562      */
1563 
reportFatalException(Exception ex)1564     protected static void reportFatalException(Exception ex) {
1565 
1566 	reportException(true, ex, new Vector());
1567 
1568 	if (table != null) {
1569 	    table.store.dumpServiceStore();
1570 	}
1571 
1572 	conf.writeLog("exiting_msg", new Object[0]);
1573 
1574 	System.exit(1);
1575 
1576     }
1577 
1578     /**
1579      * Report a nonfatal exception to the log.
1580      *
1581      * @param ex The exception to report.
1582      * @param args The method arguments.
1583      * @param store The service store being processed.
1584      */
1585 
reportNonfatalException(Exception ex, Vector args, ServiceStore store)1586     protected static void reportNonfatalException(Exception ex,
1587 						  Vector args,
1588 						  ServiceStore store) {
1589 
1590 	reportException(false, ex, args);
1591 
1592 	if (conf.traceAll()) {
1593 	    store.dumpServiceStore();
1594 	}
1595 
1596     }
1597 
1598     /**
1599      * Report an exception to the log.
1600      *
1601      * @param isFatal Indicates whether the exception is fatal or not.
1602      * @param ex The exception to report.
1603      * @param args A potentially null vector of arguments to the
1604      * 			method where the exception was caught.
1605      */
1606 
1607     private static void
reportException(boolean isFatal, Exception ex, Vector args)1608 	reportException(boolean isFatal, Exception ex, Vector args) {
1609 
1610 	StringWriter sw = new StringWriter();
1611 	PrintWriter writer = new PrintWriter(sw);
1612 
1613 	// Get the backtrace.
1614 
1615 	ex.printStackTrace(writer);
1616 
1617 	String severity = (isFatal ? "fatal_error":"nonfatal_error");
1618 	String msg = ex.getMessage();
1619 
1620 	if (msg == null) {
1621 	    msg = conf.formatMessage("no_message", new Object[0]);
1622 
1623 	} else if (ex instanceof ServiceLocationException) {
1624 	    msg = msg +
1625 		"(" + ((ServiceLocationException)ex).getErrorCode() + ")";
1626 
1627 	}
1628 
1629 	StringBuffer argMsg = new StringBuffer();
1630 
1631 	int i, n = args.size();
1632 
1633 	for (i = 0; i < n; i++) {
1634 	    argMsg.append("\n        (" + Integer.toString(i) + "):" +
1635 			  args.elementAt(i).toString());
1636 	}
1637 
1638 	conf.writeLog(severity,
1639 		      new Object[] {
1640 	    ex.getClass().getName(),
1641 		msg,
1642 		argMsg,
1643 		sw.toString()});
1644 
1645     }
1646 }
1647