1 | package org.dcache.boot; |
2 | |
3 | import java.io.IOException; |
4 | import java.io.InputStream; |
5 | import java.io.InputStreamReader; |
6 | import java.io.File; |
7 | import java.net.URI; |
8 | import java.net.URISyntaxException; |
9 | import java.util.ArrayList; |
10 | import java.util.List; |
11 | import java.util.Map; |
12 | |
13 | import org.dcache.util.DeprecatableProperties; |
14 | import org.dcache.util.NetworkUtils; |
15 | import org.dcache.util.ReplaceableProperties; |
16 | |
17 | import dmg.cells.nucleus.CellShell; |
18 | import dmg.cells.nucleus.SystemCell; |
19 | import dmg.util.Args; |
20 | import dmg.util.CommandException; |
21 | |
22 | import org.slf4j.LoggerFactory; |
23 | import org.slf4j.Logger; |
24 | import ch.qos.logback.classic.LoggerContext; |
25 | import ch.qos.logback.classic.joran.JoranConfigurator; |
26 | import ch.qos.logback.core.joran.spi.JoranException; |
27 | import ch.qos.logback.core.util.StatusPrinter; |
28 | |
29 | /** |
30 | * Domain encapsulates the configuration of a domain and its |
31 | * services. Provides the logic for starting a domain. |
32 | */ |
33 | public class Domain |
34 | { |
35 | private static final String PROPERTY_DOMAIN_NAME = "domain.name"; |
36 | private static final String PROPERTY_DOMAIN_SERVICE = "domain.service"; |
37 | private static final String PROPERTY_DOMAIN_SERVICE_URI = "domain.service.uri"; |
38 | private static final String PROPERTY_DOMAIN_PRELOAD = "domain.preload"; |
39 | |
40 | private static final String PROPERTY_LOG_CONFIG = "dcache.log.configuration"; |
41 | |
42 | private static final String SCHEME_FILE = "file"; |
43 | private static final String SUFFIX_PROPERTIES = ".properties"; |
44 | private static final String SUFFIX_XML = ".xml"; |
45 | |
46 | private static final Logger _log = |
47 | LoggerFactory.getLogger(SystemCell.class); |
48 | |
49 | private final ReplaceableProperties _properties; |
50 | private final List<ReplaceableProperties> _services; |
51 | |
52 | public Domain(String name, ReplaceableProperties defaults) |
53 | { |
54 | _properties = new DeprecatableProperties(defaults); |
55 | _properties.put(PROPERTY_DOMAIN_NAME, name); |
56 | _services = new ArrayList<ReplaceableProperties>(); |
57 | } |
58 | |
59 | public ReplaceableProperties properties() |
60 | { |
61 | return _properties; |
62 | } |
63 | |
64 | public ReplaceableProperties createService(String name) |
65 | { |
66 | ReplaceableProperties service = |
67 | new DeprecatableProperties(_properties); |
68 | service.put(PROPERTY_DOMAIN_SERVICE, name); |
69 | _services.add(service); |
70 | return service; |
71 | } |
72 | |
73 | public String getName() |
74 | { |
75 | return _properties.getReplacement(PROPERTY_DOMAIN_NAME); |
76 | } |
77 | |
78 | List<ReplaceableProperties> getServices() |
79 | { |
80 | return _services; |
81 | } |
82 | |
83 | public void start() |
84 | throws URISyntaxException, CommandException, IOException |
85 | { |
86 | initializeLogging(); |
87 | |
88 | String domainName = getName(); |
89 | SystemCell systemCell = new SystemCell(domainName); |
90 | _log.info("Starting " + domainName); |
91 | |
92 | importUnscopedParameters(systemCell, _properties); |
93 | executePreload(systemCell); |
94 | for (ReplaceableProperties serviceConfig: _services) { |
95 | executeService(systemCell, serviceConfig); |
96 | } |
97 | |
98 | if (_services.isEmpty()) { |
99 | _log.warn("No services found. Domain appears to be empty."); |
100 | } |
101 | } |
102 | |
103 | private void initializeLogging() |
104 | throws URISyntaxException, IOException |
105 | { |
106 | try { |
107 | String property = _properties.getReplacement(PROPERTY_LOG_CONFIG); |
108 | if (property == null) return; |
109 | |
110 | URI uri = new URI(property); |
111 | String path = uri.getPath(); |
112 | if (path == null) { |
113 | throw new URISyntaxException(property, "Path is missing"); |
114 | } |
115 | |
116 | if (uri.getScheme() == null || uri.getScheme().equals(SCHEME_FILE)) { |
117 | File f = new File(path); |
118 | uri = f.toURI(); |
119 | } |
120 | |
121 | LoggerContext loggerContext = |
122 | (LoggerContext) LoggerFactory.getILoggerFactory(); |
123 | loggerContext.reset(); |
124 | try { |
125 | JoranConfigurator configurator = new JoranConfigurator(); |
126 | configurator.setContext(loggerContext); |
127 | configurator.doConfigure(NetworkUtils.toURL(uri)); |
128 | } finally { |
129 | StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); |
130 | } |
131 | } catch (JoranException e) { |
132 | throw new IOException("Failed to load log configuration:" + e.getMessage(), e); |
133 | } |
134 | } |
135 | |
136 | /** |
137 | * Returns whether a name is scoped. |
138 | * |
139 | * A scoped name begins with the name of the scope followed by the |
140 | * scoping operator, a forward slash. |
141 | */ |
142 | private boolean isScoped(String name) |
143 | { |
144 | return name.indexOf('/') > -1; |
145 | } |
146 | |
147 | /** |
148 | * Returns whether a name has a particular scope. |
149 | */ |
150 | private boolean isScoped(String scope, String name) |
151 | { |
152 | return scope.length() < name.length() && |
153 | name.startsWith(scope) && name.charAt(scope.length()) == '/'; |
154 | } |
155 | |
156 | /** |
157 | * Returns the unscoped name. |
158 | */ |
159 | private String stripScope(String name) |
160 | { |
161 | int pos = name.indexOf('/'); |
162 | return (pos == -1) ? name : name.substring(pos + 1); |
163 | } |
164 | |
165 | /** |
166 | * Imports unscoped parameters into a SystemCell. |
167 | */ |
168 | private void importUnscopedParameters(SystemCell cell, |
169 | ReplaceableProperties properties) |
170 | { |
171 | Map<String,Object> domainContext = cell.getDomainContext(); |
172 | for (String key: properties.stringPropertyNames()) { |
173 | if (!isScoped(key)) { |
174 | domainContext.put(key, properties.getReplacement(key)); |
175 | } |
176 | } |
177 | } |
178 | |
179 | /** |
180 | * Imports service scoped parameters into a CellShell. |
181 | */ |
182 | private void importScopedParameters(CellShell shell, |
183 | ReplaceableProperties properties) |
184 | throws CommandException |
185 | { |
186 | Map<String,Object> environment = shell.environment(); |
187 | String service = properties.getReplacement(PROPERTY_DOMAIN_SERVICE); |
188 | for (String key: properties.stringPropertyNames()) { |
189 | if (isScoped(service, key)) { |
190 | environment.put(stripScope(key), properties.getReplacement(key)); |
191 | } |
192 | } |
193 | } |
194 | |
195 | /** |
196 | * Imports service local parameters into a CellShell. |
197 | */ |
198 | private void importLocalParameters(CellShell shell, |
199 | ReplaceableProperties properties) |
200 | throws CommandException |
201 | { |
202 | Map<String,Object> environment = shell.environment(); |
203 | for (Object o: properties.keySet()) { |
204 | String key = (String) o; |
205 | if (!isScoped(key)) { |
206 | environment.put(key, properties.getReplacement(key)); |
207 | } |
208 | } |
209 | } |
210 | |
211 | /** |
212 | * Executes a preload batch script, if defined. |
213 | */ |
214 | private void executePreload(SystemCell cell) |
215 | throws URISyntaxException, IOException, CommandException |
216 | { |
217 | String preload = _properties.getReplacement(PROPERTY_DOMAIN_PRELOAD); |
218 | if (preload != null) { |
219 | CellShell shell = new CellShell(cell.getNucleus()); |
220 | executeBatchFile(shell, new URI(preload)); |
221 | } |
222 | } |
223 | |
224 | /** |
225 | * Executes the batch file of the service. |
226 | */ |
227 | private void executeService(SystemCell cell, ReplaceableProperties service) |
228 | throws URISyntaxException, IOException, CommandException |
229 | { |
230 | /* The per service configuration is loaded into the |
231 | * environment of the CellShell used to execute the batch |
232 | * file. |
233 | */ |
234 | CellShell shell = new CellShell(cell.getNucleus()); |
235 | importScopedParameters(shell, service); |
236 | importLocalParameters(shell, service); |
237 | |
238 | URI uri = new URI(service.getReplacement(PROPERTY_DOMAIN_SERVICE_URI)); |
239 | executeBatchFile(shell, uri); |
240 | } |
241 | |
242 | /** |
243 | * Executes the batch file in the resource. |
244 | */ |
245 | private void executeBatchFile(CellShell shell, URI resource) |
246 | throws URISyntaxException, IOException, CommandException |
247 | { |
248 | InputStream input = NetworkUtils.toURL(resource).openStream(); |
249 | try { |
250 | shell.execute(resource.toString(), new InputStreamReader(input), |
251 | new Args("")); |
252 | } finally { |
253 | input.close(); |
254 | } |
255 | } |
256 | } |