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
35package com.sun.slp;
36
37import java.util.*;
38import 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
50class 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
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
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
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
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
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