1 | package org.dcache.boot; |
2 | |
3 | import java.io.BufferedReader; |
4 | import java.io.IOException; |
5 | import java.io.InputStreamReader; |
6 | import java.io.LineNumberReader; |
7 | import java.io.PrintStream; |
8 | import java.io.StringReader; |
9 | import java.net.URI; |
10 | import java.net.URISyntaxException; |
11 | import java.net.URL; |
12 | import java.util.Collection; |
13 | import java.util.Collections; |
14 | import java.util.LinkedHashMap; |
15 | import java.util.Map; |
16 | import java.util.Properties; |
17 | import java.util.Set; |
18 | import java.util.regex.Matcher; |
19 | import java.util.regex.Pattern; |
20 | |
21 | import org.dcache.util.DeprecatableProperties; |
22 | import org.dcache.util.NetworkUtils; |
23 | import org.dcache.util.ReplaceableProperties; |
24 | import org.dcache.commons.util.Strings; |
25 | |
26 | /** |
27 | * Layout encapsulates the configuration of a set of domains. |
28 | */ |
29 | public class Layout |
30 | { |
31 | private static final int READ_AHEAD_LIMIT = 256; |
32 | |
33 | private static final Pattern SECTION_HEADER = |
34 | Pattern.compile("^\\s*\\[([^\\]/]+)(/([^\\]/]+))?\\]\\s*$"); |
35 | |
36 | private static final String PROPERTY_DOMAINS = |
37 | "dcache.domains"; |
38 | |
39 | private final ReplaceableProperties _properties; |
40 | private final Map<String,Domain> _domains = |
41 | new LinkedHashMap<String,Domain>(); |
42 | |
43 | public Layout(ReplaceableProperties config) |
44 | { |
45 | _properties = new DeprecatableProperties(config); |
46 | } |
47 | |
48 | public ReplaceableProperties properties() |
49 | { |
50 | return _properties; |
51 | } |
52 | |
53 | public Collection<Domain> getDomains() |
54 | { |
55 | return Collections.unmodifiableCollection(_domains.values()); |
56 | } |
57 | |
58 | public Domain getDomain(String name) |
59 | { |
60 | return _domains.get(name); |
61 | } |
62 | |
63 | public Domain createDomain(String name) |
64 | { |
65 | Domain domain = _domains.get(name); |
66 | if (domain == null) { |
67 | domain = new Domain(name, _properties); |
68 | _domains.put(name, domain); |
69 | _properties.put(PROPERTY_DOMAINS, |
70 | Strings.join(_domains.keySet(), " ")); |
71 | } |
72 | return domain; |
73 | } |
74 | |
75 | /** |
76 | * Reads a layout definition from the URI. |
77 | * |
78 | * @param uri The URI of the layout definition. |
79 | */ |
80 | public void load(URI uri) |
81 | throws URISyntaxException, IOException |
82 | { |
83 | URL url = NetworkUtils.toURL(uri); |
84 | LineNumberReader reader = |
85 | new LineNumberReader(new InputStreamReader(url.openStream())); |
86 | try { |
87 | load(reader); |
88 | } catch (IllegalArgumentException e) { |
89 | throw new IllegalArgumentException(String.format("%s, line %d: %s", uri, reader.getLineNumber(), e.getMessage()), e); |
90 | } catch (IOException e) { |
91 | throw new IOException(String.format("%s, line %d: %s", uri, reader.getLineNumber(), e.getMessage()), e); |
92 | } catch (RuntimeException e) { |
93 | throw new RuntimeException(String.format("%s, line %d: %s", uri, reader.getLineNumber(), e.getMessage()), e); |
94 | } finally { |
95 | reader.close(); |
96 | } |
97 | } |
98 | |
99 | /** |
100 | * Reads a layout definition from the input character stream. |
101 | * |
102 | * @param reader the input character stream. |
103 | */ |
104 | public void load(BufferedReader reader) |
105 | throws IOException |
106 | { |
107 | loadSection(reader, _properties); |
108 | |
109 | String s; |
110 | while ((s = reader.readLine()) != null) { |
111 | Matcher matcher = SECTION_HEADER.matcher(s); |
112 | if (!matcher.matches()) { |
113 | throw new RuntimeException("Bug detected: Section header expected: " + s); |
114 | } |
115 | |
116 | String domainName = |
117 | _properties.replaceKeywords(matcher.group(1)); |
118 | String serviceName = |
119 | matcher.group(3); |
120 | |
121 | if (serviceName == null) { |
122 | Domain domain = createDomain(domainName); |
123 | loadSection(reader, domain.properties()); |
124 | } else { |
125 | Domain domain = getDomain(domainName); |
126 | if (domain == null) { |
127 | throw new IllegalArgumentException(String.format("Service declaration %s/%s lacks definition of domain %s", domainName, serviceName, domainName)); |
128 | } |
129 | loadSection(reader, domain.createService(serviceName)); |
130 | } |
131 | } |
132 | } |
133 | |
134 | /** |
135 | * Reads properties until the next section header. The position is |
136 | * advanced until the next section header or the end of file. |
137 | * |
138 | * @param reader The reader to read from |
139 | * @param config The Properties to which to add the properties |
140 | */ |
141 | private void loadSection(BufferedReader reader, Properties config) |
142 | throws IOException |
143 | { |
144 | String s; |
145 | StringBuilder section = new StringBuilder(); |
146 | reader.mark(READ_AHEAD_LIMIT); |
147 | while ( (s = reader.readLine()) != null && |
148 | !SECTION_HEADER.matcher(s).matches()) { |
149 | section.append(s).append('\n'); |
150 | reader.mark(READ_AHEAD_LIMIT); |
151 | } |
152 | reader.reset(); |
153 | config.load(new StringReader(section.toString())); |
154 | } |
155 | |
156 | /** |
157 | * Prints the list of domains in the layout. |
158 | */ |
159 | public void printDomainNames(PrintStream out) |
160 | { |
161 | for (Domain domain: _domains.values()) { |
162 | out.println(domain.getName()); |
163 | } |
164 | } |
165 | |
166 | /** |
167 | * Prints the domains in the layout matching one of the |
168 | * patterns. |
169 | */ |
170 | public void printMatchingDomainNames(PrintStream out, Collection<Pattern> patterns) |
171 | { |
172 | for (Domain domain: _domains.values()) { |
173 | for (Pattern pattern: patterns) { |
174 | String name = domain.getName(); |
175 | if (pattern.matcher(name).matches()) { |
176 | out.println(name); |
177 | break; |
178 | } |
179 | } |
180 | } |
181 | } |
182 | } |