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 //  ServiceStoreFactory.java: Factory for creating ServiceStore objects.
28 //  Author:           James Kempf
29 //  Created On:       Fri Apr 17 12:14:12 1998
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Mon Jan  4 15:26:34 1999
32 //  Update Count:     34
33 //
34 
35 package com.sun.slp;
36 
37 import java.util.*;
38 import java.io.*;
39 
40 /**
41  * The ServiceStoreFactory provides a way to obtain a ServiceStore
42  * object. The exact implementation will depend on how the
43  * DA/slpd is configured. It could be an in-memory database,
44  * a connection to an LDAP server, or a persistent object
45  * database.
46  *
47  * @author James Kempf
48  */
49 
50 class ServiceStoreFactory extends Object {
51 
52     private static final String DEFAULT_SERVICE_STORE =
53 	"com.sun.slp.ServiceStoreInMemory";
54 
55     private static final String SERVICE_STORE_PROPERTY =
56 	"sun.net.slp.serviceStoreClass";
57 
58     // Comment characters for deserialization.
59 
60     final private static char COMMENT_CHAR1 = '#';
61     final private static char COMMENT_CHAR2 = ';';
62 
63     // Character for URL list separator.
64 
65     final private static String URL_LIST_SEP = ", ";
66 
67     // Identifies scopes pseudo-attribute.
68 
69     final private static String SCOPES_ATTR_ID = "scopes";
70 
71     /**
72      * Return the ServiceStore for the SLP agent.
73      *
74      * @return An object supporting the ServiceStore interface.
75      * @exception ServiceLocationException Thrown
76      *			if the ServiceStore object can't be
77      *			created or if the
78      *			class implementing the ServiceStore required
79      *			a network connnection (for example, an LDAP
80      *			server) and the connection couldn't be made.
81      */
82 
createServiceStore()83     static ServiceStore createServiceStore()
84 	throws ServiceLocationException {
85 
86 	return createServiceStoreFromProperty(SERVICE_STORE_PROPERTY);
87 
88     }
89 
90     // Create the appropriate ServiceStore object from the property.
91 
92     private static ServiceStore
createServiceStoreFromProperty(String property)93 	createServiceStoreFromProperty(String property)
94 	throws ServiceLocationException {
95 
96 	Properties props = System.getProperties();
97 	String storeClassName =
98 	    props.getProperty(property,
99 			      DEFAULT_SERVICE_STORE);
100 	Class storeClass = null;
101 
102 	try {
103 
104 	    storeClass = Class.forName(storeClassName);
105 
106 	} catch (ClassNotFoundException ex) {
107 
108 	    throw
109 		new ServiceLocationException(
110 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
111 				"ssf_no_class",
112 				new Object[] {storeClassName});
113 	}
114 
115 	ServiceStore store = null;
116 
117 	try {
118 
119 	    store = (ServiceStore)storeClass.newInstance();
120 
121 	} catch (InstantiationException ex) {
122 
123 	    throw
124 		new ServiceLocationException(
125 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
126 				"ssf_inst_ex",
127 				new Object[] {
128 		    storeClassName,
129 			ex.getMessage()});
130 
131 	} catch (IllegalAccessException ex) {
132 
133 	    throw
134 		new ServiceLocationException(
135 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
136 				"ssf_ill_ex",
137 				new Object[] {
138 		    storeClassName,
139 			ex.getMessage()});
140 
141 	} catch (ClassCastException ex) {
142 
143 	    throw
144 		new ServiceLocationException(
145 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
146 				"ssf_class_cast",
147 				new Object[] {storeClassName});
148 	}
149 
150 	return store;
151     }
152 
153     /**
154      * Deserialize a service store from the open stream.
155      *
156      * @param is The object input stream for the service store.
157      * @return ServiceStore deserialized from the stream.
158      * @exception ServiceLocationException If anything goes
159      *				wrong with the deserialization.
160      */
161 
deserializeServiceStore(BufferedReader is)162     static ServiceStore deserializeServiceStore(BufferedReader is)
163 	throws ServiceLocationException {
164 
165 	ServiceStore ss = new ServiceStoreInMemory();
166 
167 	try {
168 
169 	    deserialize(is, ss);
170 
171 	} catch (IOException ex) {
172 	    throw
173 		new ServiceLocationException(
174 				ServiceLocationException.PARSE_ERROR,
175 				"ssf_io_deser",
176 				new Object[] {ex.getMessage()});
177 
178 	}
179 
180 	return ss;
181     }
182 
183     // Read the service store in the standard format from the input
184 
deserialize(BufferedReader in, ServiceStore store)185     private static void deserialize(BufferedReader in, ServiceStore store)
186 	throws IOException, ServiceLocationException {
187 
188 	SLPConfig conf = SLPConfig.getSLPConfig();
189 	int linecount = 0;
190 	int scopeLinenum = 0;
191 
192 	// Parse input file until no bytes left.
193 
194 	while (in.ready()) {
195 	    linecount++;
196 	    String line = in.readLine().trim();
197 
198 	    // Skip any empty lines at this level.
199 
200 	    if (line.length() <= 0) {
201 		continue;
202 
203 	    }
204 
205 	    char cc = line.charAt(0);
206 
207 	    // If initial character is "#" or ";", ignore the line.
208 	    //  It's a comment. Also if the line is empty.
209 
210 	    if (cc == COMMENT_CHAR1 ||
211 		cc == COMMENT_CHAR2) {
212 		continue;
213 	    }
214 
215 	    // At this level, the line must be a URL registration,
216 	    //  with format:
217 	    //
218 	    // service-url ", " language ", " lifetime [ ", " type ]
219 	    //
220 	    //
221 	    //  We allow arbitrary whitespace around commas.
222 
223 	    StringTokenizer tk = new StringTokenizer(line, URL_LIST_SEP);
224 	    String surl = null;
225 	    String slang = null;
226 	    String slifetime = null;
227 	    String sType = null;
228 
229 	    if (tk.hasMoreTokens()) {
230 		surl = tk.nextToken().trim();
231 
232 		if (tk.hasMoreTokens()) {
233 		    slang = tk.nextToken().trim();
234 
235 		    if (tk.hasMoreTokens()) {
236 			slifetime = tk.nextToken().trim();
237 
238 			if (tk.hasMoreTokens()) {
239 			    sType = tk.nextToken().trim();
240 
241 			    if (tk.hasMoreTokens()) {
242 				slang = null;
243 					// should be nothing more on the line.
244 
245 			    }
246 			}
247 		    }
248 		}
249 	    }
250 
251 	    // Check for errors.
252 
253 	    if (surl == null || slifetime == null || slang == null) {
254 		throw
255 		    new ServiceLocationException(
256 				ServiceLocationException.PARSE_ERROR,
257 				"ssf_not_valid_url",
258 				new Object[] {line});
259 	    }
260 
261 	    // Create the service: URL.
262 
263 	    Locale locale = SLPConfig.langTagToLocale(slang);
264 	    ServiceURL url = null;
265 
266 	    try {
267 
268 		int lifetime = Integer.parseInt(slifetime);
269 
270 		// If lifetime is maximum, then set to LIFETIME_PERMANENT.
271 
272 		if (lifetime == ServiceURL.LIFETIME_MAXIMUM) {
273 		    lifetime = ServiceURL.LIFETIME_PERMANENT;
274 
275 		}
276 
277 		url = new ServiceURL(surl, lifetime);
278 
279 		if (sType != null) {
280 
281 		    // Check if it's OK for this service URL.
282 
283 		    ServiceType utype = url.getServiceType();
284 
285 		    if (utype.isServiceURL()) {
286 			conf.writeLog("ssf_set_servc_err",
287 				      new Object[] {
288 			    surl,
289 				utype});
290 
291 		    } else {
292 			ServiceType t = new ServiceType(sType);
293 
294 			if (!t.isServiceURL() &&
295 			    !t.equals(url.getServiceType())) {
296 			    url.setServiceType(t);
297 
298 			}
299 
300 		    }
301 		}
302 
303 	    } catch (NumberFormatException ex) {
304 		throw
305 		    new ServiceLocationException(
306 				ServiceLocationException.PARSE_ERROR,
307 				"ssf_not_valid_lifetime",
308 				new Object[] {
309 			slifetime, new Integer(linecount)});
310 
311 	    } catch (IllegalArgumentException ex) {
312 		throw
313 		    new ServiceLocationException(
314 				ServiceLocationException.PARSE_ERROR,
315 				"ssf_syntax_err",
316 				new Object[] {
317 			ex.getMessage(), new Integer(linecount)});
318 
319 	    }
320 
321 	    // Get attributes. Format should be:
322 	    //
323 	    //      attr-line    = attr-assign | keyword
324 	    //	attr-assign  = attr-id "=" attrval-list
325 	    //	keyword      = attr-id
326 	    //	attrval-list = attrval | attrval ", " attrval-list
327 
328 	    Vector attrs = new Vector();
329 	    Hashtable ht = new Hashtable();
330 	    ServiceLocationAttribute scopeAttr = null;
331 	    boolean firstLine = true;
332 
333 	    try {
334 		while (in.ready()) {
335 		    linecount++;
336 		    line = in.readLine();
337 
338 		    // Empty line indicates we're done with attributes.
339 
340 		    if (line.length() <= 0) {
341 			break;
342 		    }
343 
344 		    // Format the line for creating. Check whether it's a
345 		    // keyword or not.
346 
347 		    if (line.indexOf("=") != -1) {
348 			line = "(" + line + ")";
349 
350 		    }
351 
352 		    // Create the attribute from the string.
353 
354 		    ServiceLocationAttribute attr =
355 			new ServiceLocationAttribute(line, false);
356 
357 		    // If this is the scope attribute, save until later.
358 
359 		    if (firstLine) {
360 			firstLine = false;
361 
362 			if (attr.getId().equalsIgnoreCase(SCOPES_ATTR_ID)) {
363 			    scopeAttr = attr;
364 			    continue; // do NOT save as a regular attribute.
365 
366 			}
367 		    }
368 
369 		    ServiceLocationAttribute.mergeDuplicateAttributes(attr,
370 								      ht,
371 								      attrs,
372 								      false);
373 
374 		}
375 	    } catch (ServiceLocationException e) {
376 		// tack on the line count
377 		e.makeAddendum(" (line " + linecount + ")");
378 		throw e;
379 
380 	    }
381 
382 	    Vector scopes = null;
383 
384 	    // Use scopes we've been configured with if none.
385 
386 	    if (scopeAttr == null) {
387 		scopes = conf.getSAConfiguredScopes();
388 
389 	    } else {
390 
391 		scopes = (Vector)scopeAttr.getValues();
392 
393 		try {
394 		    // Unescape scope strings.
395 
396 		    SLPHeaderV2.unescapeScopeStrings(scopes);
397 
398 		    // Validate, lower case scope names.
399 
400 		    DATable.validateScopes(scopes, locale);
401 
402 		} catch (ServiceLocationException e) {
403 		    e.makeAddendum(" (line " + scopeLinenum + ")");
404 		    throw e;
405 		}
406 
407 	    }
408 
409 	    // We've got the attributes, the service URL, scope, and
410 	    //  locale, so add a record. Note that any crypto is
411 	    //  added when the registration is actually done.
412 
413 	    store.register(url, attrs, scopes, locale, null, null);
414 
415 	    // Create a CSrvReg for forwarding
416 	    CSrvReg creg = new CSrvReg(true, locale, url, scopes,
417 				       attrs, null, null);
418 
419 	    ServerDATable daTable = ServerDATable.getServerDATable();
420 	    daTable.forwardSAMessage(creg, conf.getLoopback());
421 
422 	}
423     }
424 
425     // Write the service store in the standard format to the output
426     // stream.
427 
serialize(BufferedWriter out, ServiceStore store)428     static void serialize(BufferedWriter out, ServiceStore store)
429 	throws IOException, ServiceLocationException {
430 
431 	Enumeration recs = store.getServiceRecordsByScope(null);
432 
433 	while (recs.hasMoreElements()) {
434 	    ServiceStore.ServiceRecord rec =
435 		(ServiceStore.ServiceRecord)recs.nextElement();
436 	    ServiceURL url = rec.getServiceURL();
437 	    String surl = url.toString();
438 	    Vector attrs = (Vector)rec.getAttrList().clone();
439 	    Locale locale = rec.getLocale();
440 	    Vector scopes = rec.getScopes();
441 	    StringBuffer line = new StringBuffer();
442 
443 	    // Compose the registration line.
444 
445 	    line.append(surl);
446 	    line.append(", ");
447 	    line.append(SLPConfig.localeToLangTag(locale));
448 	    line.append(", ");
449 	    line.append(Integer.toString(url.getLifetime()));
450 
451 	    // Put out the service type and naming authority if the
452 	    //  URL is not a service URL.
453 
454 	    if (!surl.startsWith(Defaults.SERVICE_PREFIX)) {
455 		ServiceType type = url.getServiceType();
456 
457 		line.append(", ");
458 		line.append(type.toString());
459 
460 	    }
461 
462 	    // Write out line.
463 
464 	    out.write(line.toString(), 0, line.length());
465 	    out.newLine();
466 
467 	    // Zero line buffer.
468 
469 	    line.setLength(0);
470 
471 	    // Insert a scope attribute, if the scope isn't simply "DEFAULT".
472 
473 	    if (scopes.size() > 1 &&
474 		!Defaults.DEFAULT_SCOPE.equals((String)scopes.elementAt(0))) {
475 		attrs.insertElementAt(
476 				new ServiceLocationAttribute(SCOPES_ATTR_ID,
477 							     scopes),
478 				0);
479 	    }
480 
481 	    // Write out the attributes.
482 
483 	    int i, n = attrs.size();
484 
485 	    for (i = 0; i < n; i++) {
486 		ServiceLocationAttribute attr =
487 		    (ServiceLocationAttribute)attrs.elementAt(i);
488 		Vector vals = attr.getValues();
489 
490 		line.append(
491 		ServiceLocationAttribute.escapeAttributeString(attr.getId(),
492 							       false));
493 		// Add the escaped values.
494 
495 		if (vals != null) {
496 
497 		    line.append("=");
498 
499 		    int j, m = vals.size();
500 
501 		    for (j = 0; j < m; j++) {
502 			Object v = vals.elementAt(j);
503 
504 			if (j > 0) {
505 			    line.append(", ");
506 
507 			}
508 
509 			line.append(ServiceLocationAttribute.escapeValue(v));
510 
511 		    }
512 		}
513 
514 		out.write(line.toString(), 0, line.length());
515 		out.newLine();
516 
517 		// Zero out string buffer.
518 
519 		line.setLength(0);
520 
521 	    }
522 
523 	    // End of registration.
524 
525 	    out.newLine();
526 	}
527 
528 	out.flush();
529     }
530 
531 }
532