001: /*
002: * <copyright>
003: *
004: * Copyright 2002-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.wp.bootstrap;
028:
029: import java.io.BufferedReader;
030: import java.io.FileNotFoundException;
031: import java.io.InputStream;
032: import java.io.InputStreamReader;
033: import java.io.Reader;
034: import java.net.URI;
035: import java.util.ArrayList;
036: import java.util.Collections;
037: import java.util.Enumeration;
038: import java.util.HashMap;
039: import java.util.HashSet;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Map;
043: import java.util.Properties;
044: import java.util.Set;
045: import java.util.regex.Matcher;
046: import java.util.regex.Pattern;
047: import org.cougaar.bootstrap.SystemProperties;
048: import org.cougaar.core.service.wp.AddressEntry;
049: import org.cougaar.core.util.UID;
050: import org.cougaar.util.ConfigFinder;
051: import org.cougaar.util.log.Logger;
052: import org.cougaar.util.log.LoggerFactory;
053:
054: /**
055: * This utility class reads "-Dorg.cougaar.name.server" system
056: * properties and the "alpreg.ini" for bootstrap {@link Bundle}s
057: * provided by the {@link ConfigService}.
058: *
059: * @see #parse(String) detailed parsing notes
060: */
061: public class ConfigReader {
062:
063: private static final String FILENAME = "alpreg.ini";
064: private static final String SERVER_PROP = "org.cougaar.name.server";
065: private static final String PROP_PREFIX = SERVER_PROP + ".";
066: private static final String DEFAULT_SCHEME = "rmi";
067: private static final long DEFAULT_TTD = 240000;
068: // cougaar.org's IP with the first four bits masked to 1100:
069: private static final String DEFAULT_MULTICAST_URI = "224.22.165.34:7777";
070:
071: private static final Object lock = new Object();
072:
073: private static Map bundles;
074:
075: // these are only used if "bundles" is null
076: private static Logger logger;
077: private static String host;
078: private static int port;
079:
080: public static Map getBundles() {
081: synchronized (lock) {
082: ensureLoad();
083: return bundles;
084: }
085: }
086:
087: private static void ensureLoad() {
088: if (bundles == null) {
089: bundles = new HashMap();
090: readConfig();
091: if (bundles.isEmpty()) {
092: bundles = Collections.EMPTY_MAP;
093: return;
094: }
095: bundles = Collections.unmodifiableMap(bundles);
096: }
097: }
098:
099: private static void readConfig() {
100: logger = LoggerFactory.getInstance().createLogger(
101: ConfigReader.class);
102: if (logger.isDetailEnabled()) {
103: logger.detail("reading config");
104: }
105: try {
106: read_file(FILENAME);
107: read_props();
108: } catch (Exception e) {
109: if (logger.isErrorEnabled()) {
110: logger.error("Failed readConfig", e);
111: }
112: }
113: if (logger.isInfoEnabled()) {
114: logger.info("Read config: " + bundles);
115: }
116: logger = null;
117: }
118:
119: private static void add(String line) {
120: Object o = parse(line);
121: if (o == null) {
122: return;
123: }
124: if (logger.isDetailEnabled()) {
125: logger.detail("parsed " + line + " as " + o);
126: }
127: if (o instanceof Bundle) {
128: add((Bundle) o);
129: } else if (o instanceof AddressEntry) {
130: add((AddressEntry) o);
131: } else if (o instanceof List) {
132: add((List) o);
133: } else {
134: throw new RuntimeException("Illegal type: " + o);
135: }
136: }
137:
138: private static void add(Bundle b) {
139: if (b == null) {
140: return;
141: }
142: String name = b.getName();
143: if (name == null) {
144: return;
145: }
146: Bundle oldB = (Bundle) bundles.get(name);
147: if (b.equals(oldB)) {
148: return;
149: }
150: bundles.put(name, b);
151: if (logger.isDebugEnabled()) {
152: logger
153: .debug("Adding "
154: + b
155: + (oldB == null ? ""
156: : ", which overwrites " + oldB));
157: }
158: }
159:
160: private static void add(List l) {
161: int n = (l == null ? 0 : l.size());
162: if (n == 0) {
163: return;
164: }
165: if (n == 1) {
166: add((AddressEntry) l.get(0));
167: return;
168: }
169: Map m = new HashMap();
170: for (int i = 0; i < n; i++) {
171: AddressEntry ae = (AddressEntry) l.get(i);
172: String name = ae.getName();
173: Map m2 = (Map) m.get(name);
174: if (m2 == null) {
175: m2 = new HashMap();
176: m.put(name, m2);
177: }
178: m2.put(ae.getType(), ae);
179: }
180: for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
181: Map.Entry me = (Map.Entry) iter.next();
182: String name = (String) me.getKey();
183: Map m2 = (Map) me.getValue();
184: Bundle b = new Bundle(name, null, DEFAULT_TTD, Collections
185: .unmodifiableMap(m2));
186: add(b);
187: }
188: }
189:
190: private static void add(AddressEntry ae) {
191: if (ae == null) {
192: return;
193: }
194: String name = ae.getName();
195: String type = ae.getType();
196: Bundle b = new Bundle(name, null, DEFAULT_TTD, Collections
197: .singletonMap(type, ae));
198: add(b);
199: }
200:
201: /**
202: * Parse a line of input into AddressEntries.
203: * <p>
204: * Lines starting with "#", "[", or "//" are ignored.
205: * <p>
206: * A line starting with "name=" is decoded as a compete bundle.<br>
207: * Also, a line matching:<pre>
208: * NAME={DATA}
209: * </pre>
210: * is parsed as a bundle:<pre>
211: * name=NAME ttd=DEFAULT_TTD entries={DATA}
212: * </pre>
213: * For example:<pre>
214: * Foo={\
215: * http=http://bar.com:8800/$Foo,\
216: * -RMI=rmi://123.45.67.89:9876/theStubId\
217: * }
218: * </pre>
219: * See {@link BundleDecoder} for bundle parsing details.<br>
220: * <p>
221: * Another pattern is:<pre>
222: * NAME=(AGENT@)?((TYPE[:,])?SCHEME://)?URI
223: * </pre>
224: * If an AGENT is specified then an entry is created:<pre>
225: * (name=NAME type=alias uri=name:///AGENT)
226: * </pre>
227: * <i>plus</i> an entry is created for the remainer of
228: * the line:<br>
229: * If TYPE and SCHEME are missing, and URI is "multicast",
230: * then TYPE is left as null, SCHEME is set to "multicast",
231: * and the URI is set to the DEFAULT_MULTICAST_URI.<br>
232: * The default SCHEME is "rmi".</br>
233: * The default TYPE is:<pre>
234: * "-" + String.toUpperCase(SCHEME) + "_REG"
235: * </pre>
236: * The FILTER is the AGENT if specified, else "*".<br>
237: * If the SCHEME is "rmi" and the TYPE is "-RMI_REG", then
238: * a ("/"+FILTER) is appended to the URI.<br>
239: * If the SCHEME is "http" or "https" and the TYPE is "-HTTP"
240: * or "-HTTPS" (respectively), and the URI lacks a path, then
241: * the "/$"+FILTER+"/wp_bootstrap" is appended to the URI, where
242: * the default FILTER is "~".
243: * The line is then parsed to:<pre>
244: * (name=NAME type=TYPE uri=SCHEME://URI)
245: * </pre>
246: * <p>
247: * For example,<pre>
248: * Foo=Bar@qux://a:1/b
249: * </pre>
250: * is parsed as two AddressEntries:<pre>
251: * (name=Foo type=alias uri=name:///Bar)
252: * (name=Foo type=-QUX_REG uri=qux://a:1/b)
253: * </pre>
254: * <p>
255: * For backwards compatibility, the following lines are
256: * reserved:<pre>
257: * hostname=HOST
258: * address=HOST
259: * port=PORT
260: * alias= <i>ignored</i>
261: * lpsport= <i>ignored</i>
262: * </pre>
263: * HOST and PORT are saved and used at the end of the parsing to
264: * create a "WP=HOST:PORT" line.<br>
265: * Also, if the line ends in ":5555", then this is trimmed off,
266: * since this is the ancient PSP server port.
267: */
268: protected static Object parse(String line) {
269: if (logger.isDetailEnabled()) {
270: logger.detail("parse: " + line);
271: }
272: if (line.startsWith("#") || line.startsWith("[")
273: || line.startsWith("//")) {
274: return null;
275: }
276: // we could save these patterns, but we'd only use them
277: // at startup, so it's not worth the trouble.
278: String s1 = "^\\s*" + "([^\\s=@:,/]+)=" + "\\{" + "(.*)"
279: + "\\}" + "\\s*$";
280: Pattern p1 = Pattern.compile(s1);
281: Matcher m1 = p1.matcher(line);
282: if (m1.matches()) {
283: return Bundle.decode(line);
284: }
285: String s2 = "^\\s*" + "([^\\s=@:,/]+)=" + "(([^\\s=@:,/]+)@)?"
286: + "(" + "(([^\\s=@:,/]+)[:,])?" + "([^\\s=@:,/]+)://"
287: + ")?" + "([^\\s]+)" + "\\s*$";
288: Pattern p2 = Pattern.compile(s2);
289: Matcher m2 = p2.matcher(line);
290: if (!m2.matches()) {
291: return null;
292: }
293: String name = m2.group(1);
294: String agent = m2.group(3);
295: String type = m2.group(6);
296: String scheme = m2.group(7);
297: String suri = m2.group(8);
298: if (agent == null && type == null && scheme == null
299: && suri != null && suri.endsWith("@")
300: && suri.matches("[^\\s=@:,/]+@")) {
301: // fix bad pattern! just an agent name
302: agent = suri.substring(0, suri.length() - 1);
303: suri = null;
304: }
305: AddressEntry ae2 = (agent == null ? (null) : AddressEntry
306: .getAddressEntry(name, "alias", URI.create("name:///"
307: + agent)));
308: if (suri == null) {
309: return ae2;
310: }
311: if ("address".equals(name) || "hostname".equals(name)) {
312: // save for later
313: host = suri;
314: if (logger.isDetailEnabled()) {
315: logger.detail("host=" + suri);
316: }
317: return null;
318: }
319: if ("port".equals(name)) {
320: // save for later
321: port = Integer.parseInt(suri);
322: if (logger.isDetailEnabled()) {
323: logger.detail("port=" + suri);
324: }
325: return null;
326: }
327: if ("alias".equals(name) || "lpsport".equals(name)) {
328: // ignore
329: return null;
330: }
331: if (type == null && scheme == null && suri != null
332: && suri.endsWith(":5555")) {
333: // trim off the old psp server port
334: if (logger.isWarnEnabled()) {
335: logger.warn("Removing trailing :5555 from " + suri);
336: }
337: suri = suri.substring(0, suri.length() - 5);
338: }
339: if (type == null && scheme == null && "multicast".equals(suri)) {
340: scheme = suri;
341: suri = DEFAULT_MULTICAST_URI;
342: }
343: if (scheme == null) {
344: scheme = DEFAULT_SCHEME;
345: }
346: if (type == null) {
347: type = "-" + scheme.toUpperCase() + "_REG";
348: }
349: if (scheme.equals("rmi") && type.equals("-RMI_REG")) {
350: suri += "/" + (agent == null ? "*" : agent);
351: }
352: URI uri = URI.create(scheme + "://" + suri);
353: if (((scheme.equals("http") && type.equals("-HTTP_REG")) || (scheme
354: .equals("https") && type.equals("-HTTPS_REG")))
355: && (uri.getPath() == null || uri.getPath().length() == 0)) {
356: suri = suri += "/$" + (agent == null ? "~" : agent)
357: + "/wp_bootstrap";
358: uri = URI.create(scheme + "://" + suri);
359: }
360: AddressEntry ae = AddressEntry.getAddressEntry(name, type, uri);
361: if (agent == null) {
362: return ae;
363: }
364: // both AGENT@ and URI
365: List l = new ArrayList(2);
366: l.add(ae2);
367: l.add(ae);
368: return l;
369: }
370:
371: private static void read_file(String filename) {
372: if (logger.isDetailEnabled()) {
373: logger.detail("read_file(" + filename + ")");
374: }
375: try {
376: InputStream fs = ConfigFinder.getInstance().open(filename);
377: if (fs == null) {
378: if (logger.isDetailEnabled()) {
379: logger.detail("unable to open file " + filename);
380: }
381: return;
382: }
383: if (logger.isDetailEnabled()) {
384: logger.detail("reading file " + filename);
385: }
386: BufferedReader in = new BufferedReader(
387: new InputStreamReader(fs));
388: String s = null;
389: while (true) {
390: String line = in.readLine();
391: if (line == null) {
392: break;
393: }
394: boolean more = line.endsWith("\\");
395: if (more) {
396: line = line.substring(0, line.length() - 1);
397: }
398: if (s == null) {
399: s = line;
400: } else {
401: s += line;
402: }
403: if (more) {
404: continue;
405: }
406: add(s);
407: s = null;
408: }
409: if (logger.isDetailEnabled()) {
410: logger.detail("closing file " + filename);
411: }
412: in.close();
413: if (host != null && port > 0) {
414: // backwards compatibility!
415: if (logger.isDetailEnabled()) {
416: logger.detail("adding backwards compatible " + host
417: + ":" + port);
418: }
419: add("WP=" + host + ":" + port);
420: }
421: host = null;
422: port = -1;
423: } catch (FileNotFoundException fnfe) {
424: if (logger.isDetailEnabled()) {
425: logger.detail("file not found " + filename, fnfe);
426: }
427: } catch (Exception e) {
428: if (logger.isErrorEnabled()) {
429: logger.error("Failed read of " + filename, e);
430: }
431: }
432: }
433:
434: private static void read_props() {
435: if (logger.isDetailEnabled()) {
436: logger.detail("read_props()");
437: }
438:
439: String server = SystemProperties.getProperty(SERVER_PROP);
440: if (server != null) {
441: if (logger.isDetailEnabled()) {
442: logger.detail("adding \"WP=" + server + "\" from "
443: + SERVER_PROP);
444: }
445: add("WP=" + server);
446: }
447:
448: Properties props = SystemProperties
449: .getSystemPropertiesWithPrefix(PROP_PREFIX);
450: for (Enumeration en = props.propertyNames(); en
451: .hasMoreElements();) {
452: String key = (String) en.nextElement();
453: String name = key.substring(PROP_PREFIX.length());
454: String value = props.getProperty(key);
455: String line = name + "=" + value;
456: if (logger.isDetailEnabled()) {
457: logger.detail("adding \"" + line + "\" from "
458: + PROP_PREFIX + "*");
459: }
460: add(line);
461: }
462:
463: if (host != null && port > 0) {
464: // backwards compatibility!
465: if (logger.isDetailEnabled()) {
466: logger.detail("adding backwards compatible " + host
467: + ":" + port);
468: }
469: add("WP=" + host + ":" + port);
470: }
471: host = null;
472: port = -1;
473: }
474: }
|