KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > config > schema > setup > StandardXMLFileConfigurationCreator


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
3  * notice. All rights reserved.
4  */

5 package com.tc.config.schema.setup;
6
7 import org.apache.commons.io.CopyUtils;
8 import org.apache.xmlbeans.XmlError;
9 import org.apache.xmlbeans.XmlException;
10 import org.apache.xmlbeans.XmlInteger;
11 import org.xml.sax.SAXException JavaDoc;
12
13 import com.tc.config.schema.beanfactory.BeanWithErrors;
14 import com.tc.config.schema.beanfactory.ConfigBeanFactory;
15 import com.tc.config.schema.defaults.DefaultValueProvider;
16 import com.tc.config.schema.defaults.FromSchemaDefaultValueProvider;
17 import com.tc.config.schema.dynamic.ParameterSubstituter;
18 import com.tc.config.schema.repository.ApplicationsRepository;
19 import com.tc.config.schema.repository.MutableBeanRepository;
20 import com.tc.config.schema.setup.sources.ConfigurationSource;
21 import com.tc.config.schema.setup.sources.FileConfigurationSource;
22 import com.tc.config.schema.setup.sources.ResourceConfigurationSource;
23 import com.tc.config.schema.setup.sources.ServerConfigurationSource;
24 import com.tc.config.schema.setup.sources.URLConfigurationSource;
25 import com.tc.logging.CustomerLogging;
26 import com.tc.logging.TCLogger;
27 import com.tc.logging.TCLogging;
28 import com.tc.util.Assert;
29 import com.terracottatech.config.Server;
30 import com.terracottatech.config.Servers;
31 import com.terracottatech.config.TcConfigDocument;
32 import com.terracottatech.config.TcConfigDocument.TcConfig;
33
34 import java.io.ByteArrayInputStream JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.File JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.StringWriter JavaDoc;
40 import java.util.regex.Matcher JavaDoc;
41 import java.util.regex.Pattern JavaDoc;
42
43 import javax.xml.parsers.ParserConfigurationException JavaDoc;
44
45 /**
46  * A {@link ConfigurationCreator} that works off XML files, using the standard config-spec model.
47  */

48 public class StandardXMLFileConfigurationCreator implements ConfigurationCreator {
49
50   private static final TCLogger consoleLogger = CustomerLogging.getConsoleLogger();
51
52   private static final long GET_CONFIGURATION_TOTAL_TIMEOUT = 5 * 60 * 1000; // five
53
// minutes
54
private static final long GET_CONFIGURATION_ONE_SOURCE_TIMEOUT = 30 * 1000; // thirty
55
// seconds
56
private static final long MIN_RETRY_TIMEOUT = 5 * 1000; // five
57
// seconds
58

59   protected final String JavaDoc configurationSpec;
60   protected final File JavaDoc defaultDirectory;
61   protected final ConfigBeanFactory beanFactory;
62
63   private TCLogger logger;
64   private boolean loadedFromTrustedSource;
65   private String JavaDoc configDescription;
66   private File JavaDoc directoryLoadedFrom;
67
68   public StandardXMLFileConfigurationCreator(String JavaDoc configurationSpec, File JavaDoc defaultDirectory,
69                                              ConfigBeanFactory beanFactory) {
70     this(TCLogging.getLogger(StandardXMLFileConfigurationCreator.class), configurationSpec, defaultDirectory,
71         beanFactory);
72   }
73
74   public StandardXMLFileConfigurationCreator(TCLogger logger, String JavaDoc configurationSpec, File JavaDoc defaultDirectory,
75                                              ConfigBeanFactory beanFactory) {
76     Assert.assertNotBlank(configurationSpec);
77     Assert.assertNotNull(defaultDirectory);
78     Assert.assertNotNull(beanFactory);
79
80     this.logger = logger;
81     this.configurationSpec = configurationSpec;
82     this.defaultDirectory = defaultDirectory;
83     this.beanFactory = beanFactory;
84     this.configDescription = null;
85   }
86
87   private static final Pattern JavaDoc SERVER_PATTERN = Pattern.compile("(.*):(.*)", Pattern.CASE_INSENSITIVE);
88
89   private static final Pattern JavaDoc RESOURCE_PATTERN = Pattern.compile("resource://(.*)", Pattern.CASE_INSENSITIVE);
90
91   // We require more than one character before the colon so that we don't mistake Windows-style directory paths as URLs.
92
private static final Pattern JavaDoc URL_PATTERN = Pattern.compile("[A-Za-z][A-Za-z]+://.*");
93
94   private ConfigurationSource[] createConfigurationSources() throws ConfigurationSetupException {
95     String JavaDoc[] components = configurationSpec.split(",");
96     ConfigurationSource[] out = new ConfigurationSource[components.length];
97
98     for (int i = 0; i < components.length; ++i) {
99       String JavaDoc thisComponent = components[i];
100       ConfigurationSource source = null;
101
102       if (source == null) source = attemptToCreateServerSource(thisComponent);
103       if (source == null) source = attemptToCreateResourceSource(thisComponent);
104       if (source == null) source = attemptToCreateURLSource(thisComponent);
105       if (source == null) source = attemptToCreateFileSource(thisComponent);
106
107       if (source == null) {
108         // formatting
109
throw new ConfigurationSetupException("The location '" + thisComponent
110             + "' is not in any recognized format -- it doesn't " + "seem to be a server, resource, URL, or file.");
111       }
112
113       out[i] = source;
114     }
115
116     return out;
117   }
118
119   private ConfigurationSource attemptToCreateServerSource(String JavaDoc text) {
120     Matcher JavaDoc matcher = SERVER_PATTERN.matcher(text);
121     if (matcher.matches()) {
122       String JavaDoc host = matcher.group(1);
123       String JavaDoc portText = matcher.group(2);
124
125       try {
126         return new ServerConfigurationSource(host, Integer.parseInt(portText));
127       } catch (Exception JavaDoc e) {/**/
128       }
129     }
130     return null;
131   }
132
133   private ConfigurationSource attemptToCreateResourceSource(String JavaDoc text) {
134     Matcher JavaDoc matcher = RESOURCE_PATTERN.matcher(text);
135     if (matcher.matches()) return new ResourceConfigurationSource(matcher.group(1), getClass());
136     else return null;
137   }
138
139   private ConfigurationSource attemptToCreateFileSource(String JavaDoc text) {
140     return new FileConfigurationSource(text, defaultDirectory);
141   }
142
143   private ConfigurationSource attemptToCreateURLSource(String JavaDoc text) {
144     Matcher JavaDoc matcher = URL_PATTERN.matcher(text);
145     if (matcher.matches()) return new URLConfigurationSource(text);
146     else return null;
147   }
148
149   public void createConfigurationIntoRepositories(MutableBeanRepository l1BeanRepository,
150                                                   MutableBeanRepository l2sBeanRepository,
151                                                   MutableBeanRepository systemBeanRepository,
152                                                   ApplicationsRepository applicationsRepository)
153       throws ConfigurationSetupException {
154     Assert.assertNotNull(l1BeanRepository);
155     Assert.assertNotNull(l2sBeanRepository);
156     Assert.assertNotNull(systemBeanRepository);
157     Assert.assertNotNull(applicationsRepository);
158
159     ConfigurationSource[] sources = createConfigurationSources();
160     long startTime = System.currentTimeMillis();
161     ConfigurationSource[] remainingSources = new ConfigurationSource[sources.length];
162     ConfigurationSource loadedSource = null;
163     System.arraycopy(sources, 0, remainingSources, 0, sources.length);
164     long lastLoopStartTime = 0;
165     int iteration = 0;
166     InputStream JavaDoc out = null;
167     boolean trustedSource = false;
168     String JavaDoc descrip = null;
169
170     while (iteration == 0 || (System.currentTimeMillis() - startTime < GET_CONFIGURATION_TOTAL_TIMEOUT)) {
171       sleepIfNecessaryToAvoidPoundingSources(lastLoopStartTime);
172       lastLoopStartTime = System.currentTimeMillis();
173
174       for (int i = 0; i < remainingSources.length; ++i) {
175         if (remainingSources[i] == null) continue;
176         out = trySource(remainingSources, i);
177
178         if (out != null) {
179           loadedSource = remainingSources[i];
180           trustedSource = loadedSource.isTrusted();
181           descrip = loadedSource.toString();
182           break;
183         }
184       }
185
186       if (out != null) break;
187
188       ++iteration;
189
190       boolean haveSources = false;
191       for (int i = 0; i < remainingSources.length; ++i)
192         haveSources = haveSources || remainingSources[i] != null;
193       if (!haveSources) {
194         // All sources have failed; bail out.
195
break;
196       }
197     }
198
199     if (out == null) configurationFetchFailed(sources, startTime);
200
201     loadConfigurationData(out, trustedSource, descrip, l1BeanRepository, l2sBeanRepository, systemBeanRepository,
202         applicationsRepository);
203     consoleLogger.info("Configuration loaded from the " + descrip + ".");
204   }
205
206   private void configurationFetchFailed(ConfigurationSource[] sources, long startTime)
207       throws ConfigurationSetupException {
208     String JavaDoc text = "Could not fetch configuration data from ";
209     if (sources.length > 1) text += "" + sources.length + " different configuration sources";
210     else text += "the " + sources[0];
211     text += ". ";
212
213     if (sources.length > 1) {
214       text += " The sources we tried are: ";
215       for (int i = 0; i < sources.length; ++i) {
216         if (i > 0) text += ", ";
217         if (i == sources.length - 1) text += "and ";
218         text += "the " + sources[i].toString();
219       }
220       text += ". ";
221     }
222
223     if (System.currentTimeMillis() - startTime >= GET_CONFIGURATION_TOTAL_TIMEOUT) {
224       text += " Fetch attempt duration: " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds.";
225     }
226
227     text += "\n\nTo correct this problem specify a valid configuration location using the ";
228     text += "-f/--config command-line options.";
229
230     consoleLogger.error(text);
231     throw new ConfigurationSetupException(text);
232   }
233
234   private InputStream JavaDoc trySource(ConfigurationSource[] remainingSources, int i) {
235     InputStream JavaDoc out = null;
236
237     try {
238       logger.info("Attempting to load configuration from the " + remainingSources[i] + "...");
239       out = remainingSources[i].getInputStream(GET_CONFIGURATION_ONE_SOURCE_TIMEOUT);
240       directoryLoadedFrom = remainingSources[i].directoryLoadedFrom();
241     } catch (ConfigurationSetupException cse) {
242       String JavaDoc text = "We couldn't load configuration data from the " + remainingSources[i];
243       text += "; this error is permanent, so this source will not be retried.";
244
245       if (remainingSources.length > 1) text += " Skipping this source and going to the next one.";
246
247       text += " (Error: " + cse.getLocalizedMessage() + ".)";
248
249       consoleLogger.warn(text);
250
251       remainingSources[i] = null;
252     } catch (IOException JavaDoc ioe) {
253       String JavaDoc text = "We couldn't load configuration data from the " + remainingSources[i];
254
255       if (remainingSources.length > 1) {
256         text += "; this error is temporary, so this source will be retried later if configuration can't be loaded elsewhere. ";
257         text += "Skipping this source and going to the next one.";
258       } else {
259         text += "; retrying.";
260       }
261
262       text += " (Error: " + ioe.getLocalizedMessage() + ".)";
263       consoleLogger.warn(text);
264     }
265
266     return out;
267   }
268
269   private void sleepIfNecessaryToAvoidPoundingSources(long lastLoopStartTime) {
270     long delay = MIN_RETRY_TIMEOUT - (System.currentTimeMillis() - lastLoopStartTime);
271     if (delay > 0) {
272       try {
273         logger.info("Waiting " + delay + " ms until we try to get configuration data again...");
274         Thread.sleep(delay);
275       } catch (InterruptedException JavaDoc ie) {
276         // whatever
277
}
278     }
279   }
280
281   private void logCopyOfConfig(InputStream JavaDoc in, String JavaDoc descrip) throws IOException JavaDoc {
282     StringWriter JavaDoc sw = new StringWriter JavaDoc();
283     CopyUtils.copy(in, sw);
284
285     logger.info("Successfully loaded configuration from the " + descrip + ". Config is:\n\n" + sw.toString());
286   }
287
288   private void loadConfigurationData(InputStream JavaDoc in, boolean trustedSource, String JavaDoc descrip,
289                                      MutableBeanRepository clientBeanRepository,
290                                      MutableBeanRepository serversBeanRepository,
291                                      MutableBeanRepository systemBeanRepository,
292                                      ApplicationsRepository applicationsRepository) throws ConfigurationSetupException {
293     try {
294       loadedFromTrustedSource = trustedSource;
295       configDescription = descrip;
296
297       ByteArrayOutputStream JavaDoc dataCopy = new ByteArrayOutputStream JavaDoc();
298       CopyUtils.copy(in, dataCopy);
299
300       logCopyOfConfig(new ByteArrayInputStream JavaDoc(dataCopy.toByteArray()), descrip);
301
302       InputStream JavaDoc copyIn = new ByteArrayInputStream JavaDoc(dataCopy.toByteArray());
303       BeanWithErrors beanWithErrors = beanFactory.createBean(copyIn, descrip);
304
305       if (beanWithErrors.errors() != null && beanWithErrors.errors().length > 0) {
306         logger.debug("Configuration didn't parse; it had " + beanWithErrors.errors().length + " error(s).");
307
308         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
309         for (int i = 0; i < beanWithErrors.errors().length; ++i) {
310           XmlError error = beanWithErrors.errors()[i];
311           buf.append(" [" + i + "]: Line " + error.getLine() + ", column " + error.getColumn() + ": "
312               + error.getMessage() + "\n");
313         }
314
315         throw new ConfigurationSetupException("The configuration data in the " + descrip + " does not obey the "
316             + "Terracotta schema:\n" + buf);
317       } else {
318         logger.debug("Configuration is valid.");
319       }
320
321       TcConfig config = ((TcConfigDocument) beanWithErrors.bean()).getTcConfig();
322       Servers servers = config.getServers();
323       if (servers != null) {
324         Server server;
325         for (int i = 0; i < servers.sizeOfServerArray(); i++) {
326           server = servers.getServerArray(i);
327           // CDV-166: per our documentation in the schema itself, host is supposed to default to '%i' and name is
328
// supposed to default to 'host:dso-port'
329
if (!server.isSetHost() || server.getHost().trim().length() == 0) {
330             server.setHost("%i");
331           }
332           if (!server.isSetName() || server.getName().trim().length() == 0) {
333             int dsoPort = server.getDsoPort();
334             if (dsoPort == 0) {
335               // Find the default value, if we can
336
final DefaultValueProvider defaultValueProvider = new FromSchemaDefaultValueProvider();
337               if (defaultValueProvider.hasDefault(server.schemaType(), "dso-port")) {
338                 final XmlInteger defaultValue = (XmlInteger) defaultValueProvider.defaultFor(server.schemaType(),
339                     "dso-port");
340                 dsoPort = defaultValue.getBigIntegerValue().intValue();
341               }
342             }
343             server.setName(server.getHost() + (dsoPort > 0 ? ":" + dsoPort : ""));
344           }
345           // CDV-77: add parameter expansion to the <server> attributes ('host' and 'name')
346
server.setHost(ParameterSubstituter.substitute(server.getHost()));
347           server.setName(ParameterSubstituter.substitute(server.getName()));
348         }
349       }
350
351       clientBeanRepository.setBean(config.getClients(), descrip);
352       serversBeanRepository.setBean(config.getServers(), descrip);
353       systemBeanRepository.setBean(config.getSystem(), descrip);
354
355       if (config.isSetApplication()) {
356         applicationsRepository.repositoryFor(TVSConfigurationSetupManagerFactory.DEFAULT_APPLICATION_NAME).setBean(
357             config.getApplication(), descrip);
358       }
359     } catch (IOException JavaDoc ioe) {
360       throw new ConfigurationSetupException("We were unable to read configuration data from the " + descrip + ": "
361           + ioe.getLocalizedMessage(), ioe);
362     } catch (SAXException JavaDoc saxe) {
363       throw new ConfigurationSetupException("The configuration data in the " + descrip + " is not well-formed XML: "
364           + saxe.getLocalizedMessage(), saxe);
365     } catch (ParserConfigurationException JavaDoc pce) {
366       throw Assert.failure("The XML parser can't be configured correctly; this should not happen.", pce);
367     } catch (XmlException xmle) {
368       throw new ConfigurationSetupException("The configuration data in the " + descrip + " does not obey the "
369           + "Terracotta schema: " + xmle.getLocalizedMessage(), xmle);
370     }
371   }
372
373   public File JavaDoc directoryConfigurationLoadedFrom() {
374     return directoryLoadedFrom;
375   }
376
377   public boolean loadedFromTrustedSource() {
378     return loadedFromTrustedSource;
379   }
380
381   public String JavaDoc describeSources() {
382     if (configDescription == null) {
383       return "The configuration specified by '" + configurationSpec + "'";
384     } else {
385       return configDescription;
386     }
387   }
388
389 }
390
Popular Tags