001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.config.schema.setup;
006:
007: import org.apache.xmlbeans.XmlObject;
008: import org.apache.xmlbeans.XmlOptions;
009:
010: import com.tc.capabilities.AbstractCapabilitiesFactory;
011: import com.tc.capabilities.Capabilities;
012: import com.tc.config.schema.IllegalConfigurationChangeHandler;
013: import com.tc.config.schema.NewCommonL2Config;
014: import com.tc.config.schema.NewCommonL2ConfigObject;
015: import com.tc.config.schema.NewHaConfig;
016: import com.tc.config.schema.NewHaConfigObject;
017: import com.tc.config.schema.NewSystemConfig;
018: import com.tc.config.schema.NewSystemConfigObject;
019: import com.tc.config.schema.UpdateCheckConfig;
020: import com.tc.config.schema.UpdateCheckConfigObject;
021: import com.tc.config.schema.defaults.DefaultValueProvider;
022: import com.tc.config.schema.repository.ChildBeanFetcher;
023: import com.tc.config.schema.repository.ChildBeanRepository;
024: import com.tc.config.schema.utils.XmlObjectComparator;
025: import com.tc.logging.TCLogger;
026: import com.tc.logging.TCLogging;
027: import com.tc.object.config.schema.NewL2DSOConfig;
028: import com.tc.object.config.schema.NewL2DSOConfigObject;
029: import com.tc.object.config.schema.PersistenceMode;
030: import com.tc.util.Assert;
031: import com.terracottatech.config.Application;
032: import com.terracottatech.config.Client;
033: import com.terracottatech.config.Ha;
034: import com.terracottatech.config.Server;
035: import com.terracottatech.config.Servers;
036: import com.terracottatech.config.System;
037: import com.terracottatech.config.TcConfigDocument;
038: import com.terracottatech.config.UpdateCheck;
039:
040: import java.io.ByteArrayInputStream;
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.io.StringWriter;
044: import java.io.UnsupportedEncodingException;
045: import java.lang.reflect.Array;
046: import java.util.HashMap;
047: import java.util.HashSet;
048: import java.util.Map;
049: import java.util.Set;
050:
051: /**
052: * The standard implementation of {@link com.tc.config.schema.setup.L2TVSConfigurationSetupManager}.
053: */
054: public class StandardL2TVSConfigurationSetupManager extends
055: BaseTVSConfigurationSetupManager implements
056: L2TVSConfigurationSetupManager {
057:
058: private static TCLogger logger = TCLogging
059: .getLogger(StandardL2TVSConfigurationSetupManager.class);
060:
061: private final ConfigurationCreator configurationCreator;
062:
063: private NewSystemConfig systemConfig;
064: private final Map l2ConfigData;
065: private final NewHaConfig haConfig;
066: private final UpdateCheckConfig updateCheckConfig;
067:
068: private final String this L2Identifier;
069: private L2ConfigData myConfigData;
070:
071: public StandardL2TVSConfigurationSetupManager(
072: ConfigurationCreator configurationCreator,
073: String this L2Identifier,
074: DefaultValueProvider defaultValueProvider,
075: XmlObjectComparator xmlObjectComparator,
076: IllegalConfigurationChangeHandler illegalConfigChangeHandler)
077: throws ConfigurationSetupException {
078: super (defaultValueProvider, xmlObjectComparator,
079: illegalConfigChangeHandler);
080:
081: Assert.assertNotNull(configurationCreator);
082: Assert.assertNotNull(defaultValueProvider);
083: Assert.assertNotNull(xmlObjectComparator);
084:
085: this .configurationCreator = configurationCreator;
086:
087: this .systemConfig = null;
088: this .l2ConfigData = new HashMap();
089: this .haConfig = getHaConfig();
090: this .updateCheckConfig = getUpdateCheckConfig();
091:
092: this .this L2Identifier = this L2Identifier;
093: this .myConfigData = null;
094:
095: runConfigurationCreator(this .configurationCreator);
096:
097: selectL2((Servers) serversBeanRepository().bean(),
098: "the set of L2s known to us");
099: validateRestrictions();
100: }
101:
102: private NewHaConfig getHaConfig() {
103: ChildBeanRepository beanRepository = new ChildBeanRepository(
104: serversBeanRepository(), Ha.class,
105: new ChildBeanFetcher() {
106: public XmlObject getChild(XmlObject parent) {
107: return ((Servers) parent).getHa();
108: }
109: });
110:
111: return new NewHaConfigObject(
112: createContext(beanRepository, configurationCreator
113: .directoryConfigurationLoadedFrom()));
114: }
115:
116: private UpdateCheckConfig getUpdateCheckConfig() {
117: ChildBeanRepository beanRepository = new ChildBeanRepository(
118: serversBeanRepository(), UpdateCheck.class,
119: new ChildBeanFetcher() {
120: public XmlObject getChild(XmlObject parent) {
121: return ((Servers) parent).getUpdateCheck();
122: }
123: });
124:
125: return new UpdateCheckConfigObject(createContext(
126: beanRepository, configurationCreator
127: .directoryConfigurationLoadedFrom()));
128: }
129:
130: private class L2ConfigData {
131: private final String name;
132: private final ChildBeanRepository beanRepository;
133:
134: private final NewCommonL2Config commonL2Config;
135: private final NewL2DSOConfig dsoL2Config;
136:
137: public L2ConfigData(String name)
138: throws ConfigurationSetupException {
139: this .name = name;
140: findMyL2Bean(); // To get the exception in case things are screwed up
141:
142: this .beanRepository = new ChildBeanRepository(
143: serversBeanRepository(), Server.class,
144: new BeanFetcher());
145:
146: this .commonL2Config = new NewCommonL2ConfigObject(
147: createContext(this .beanRepository,
148: configurationCreator
149: .directoryConfigurationLoadedFrom()));
150: this .dsoL2Config = new NewL2DSOConfigObject(createContext(
151: this .beanRepository, configurationCreator
152: .directoryConfigurationLoadedFrom()));
153: }
154:
155: public String name() {
156: return this .name;
157: }
158:
159: public NewCommonL2Config commonL2Config() {
160: return this .commonL2Config;
161: }
162:
163: public NewL2DSOConfig dsoL2Config() {
164: return this .dsoL2Config;
165: }
166:
167: public boolean explicitlySpecifiedInConfigFile()
168: throws ConfigurationSetupException {
169: return findMyL2Bean() != null;
170: }
171:
172: private Server findMyL2Bean()
173: throws ConfigurationSetupException {
174: Servers servers = (Servers) serversBeanRepository().bean();
175: Server[] l2Array = servers == null ? null : servers
176: .getServerArray();
177:
178: if (l2Array == null || l2Array.length == 0)
179: return null;
180: else if (this .name == null) {
181: if (l2Array.length > 1) {
182: // formatting
183: throw new ConfigurationSetupException(
184: "You have not specified a name for your L2, and there are "
185: + l2Array.length
186: + " L2s defined in the configuration file. "
187: + "You must indicate which L2 this is.");
188: } else {
189: return l2Array[0];
190: }
191: } else {
192: for (int i = 0; i < l2Array.length; ++i) {
193: if (this .name.trim().equalsIgnoreCase(
194: l2Array[i].getName().trim())) {
195: return l2Array[i];
196: }
197: }
198: }
199:
200: return null;
201: }
202:
203: private class BeanFetcher implements ChildBeanFetcher {
204: public XmlObject getChild(XmlObject parent) {
205: try {
206: return findMyL2Bean();
207: } catch (ConfigurationSetupException cse) {
208: logger.warn("Unable to find L2 bean for L2 '"
209: + name + "'", cse);
210: return null;
211: }
212: }
213: }
214: }
215:
216: public String describeSources() {
217: return this .configurationCreator.describeSources();
218: }
219:
220: private synchronized L2ConfigData configDataFor(String name)
221: throws ConfigurationSetupException {
222: L2ConfigData out = (L2ConfigData) this .l2ConfigData.get(name);
223:
224: if (out == null) {
225: out = new L2ConfigData(name);
226:
227: Servers servers = (Servers) this .serversBeanRepository()
228: .bean();
229: String list = "[data unavailable]";
230:
231: if (servers != null) {
232: Server[] serverList = servers.getServerArray();
233:
234: if (serverList != null) {
235: list = "";
236:
237: for (int i = 0; i < serverList.length; ++i) {
238: if (i > 0)
239: list += ", ";
240: if (i == serverList.length - 1)
241: list += "and ";
242: list += "'" + serverList[i].getName() + "'";
243: }
244: }
245: }
246:
247: if ((!out.explicitlySpecifiedInConfigFile())
248: && name != null) {
249: // formatting
250: throw new ConfigurationSetupException(
251: "Multiple <server> elements are defined in the configuration file. "
252: + "As such, each server that you start needs to know which configuration "
253: + "it should use.\n\n"
254: + "However, this server couldn't figure out which one it is -- it thinks it's "
255: + "called '"
256: + name
257: + "' (which, by default, is the host name of this machine), but you've only "
258: + "created <server> elements in the config file called "
259: + list
260: + ".\n\nPlease re-start the server with a '-n <name>' argument on the command line to tell this "
261: + "server which one it is, or change the 'name' attributes of the <server> "
262: + "elements in the config file as appropriate.");
263: }
264:
265: this .l2ConfigData.put(name, out);
266: }
267:
268: return out;
269: }
270:
271: private void selectL2(Servers servers, final String description)
272: throws ConfigurationSetupException {
273: this .systemConfig = new NewSystemConfigObject(createContext(
274: systemBeanRepository(), configurationCreator
275: .directoryConfigurationLoadedFrom()));
276:
277: if (this .allCurrentlyKnownServers().length == 1) {
278: if (servers != null && servers.getServerArray() != null
279: && servers.getServerArray()[0] != null) {
280: this .myConfigData = configDataFor(servers
281: .getServerArray()[0].getName());
282: } else {
283: this .myConfigData = configDataFor(null);
284: }
285: } else
286: this .myConfigData = configDataFor(this .this L2Identifier);
287:
288: LogSettingConfigItemListener listener = new LogSettingConfigItemListener(
289: TCLogging.PROCESS_TYPE_L2);
290: this .myConfigData.commonL2Config().logsPath().addListener(
291: listener);
292: listener.valueChanged(null, this .myConfigData.commonL2Config()
293: .logsPath().getObject());
294: }
295:
296: private void validateRestrictions()
297: throws ConfigurationSetupException {
298: validateLicenseModuleRestrictions();
299: validateDSOClusterPersistenceMode();
300: }
301:
302: private void validateDSOClusterPersistenceMode()
303: throws ConfigurationSetupException {
304: if (super .serversBeanRepository().bean() != null) {
305: Server[] servers = ((Servers) super .serversBeanRepository()
306: .bean()).getServerArray();
307: Set badServers = new HashSet();
308:
309: if (servers != null && servers.length > 1) {
310: Capabilities capabilities = AbstractCapabilitiesFactory
311: .getCapabilitiesManager();
312:
313: if (!capabilities.hasHA()
314: && capabilities.canClusterPOJOs()) {
315: throw new ConfigurationSetupException(
316: "Attempting to run multiple servers without license "
317: + "authorization of DSO High Availability.");
318: }
319:
320: // We have clustered DSO; they must all be in permanent-store mode
321: for (int i = 0; i < servers.length; ++i) {
322: String name = servers[i].getName();
323: L2ConfigData data = configDataFor(name);
324:
325: Assert.assertNotNull(data);
326: if (!haConfig.isNetworkedActivePassive()
327: && !(data.dsoL2Config().persistenceMode()
328: .getObject()
329: .equals(PersistenceMode.PERMANENT_STORE))) {
330: badServers.add(name);
331: }
332: }
333: }
334:
335: if (badServers.size() > 0) {
336: // formatting
337: throw new ConfigurationSetupException(
338: "Your Terracotta system has a clustered DSO configuration -- i.e., "
339: + "DSO is enabled, and more than one server is defined in the configuration file -- but "
340: + "at least one server is in the '"
341: + PersistenceMode.TEMPORARY_SWAP_ONLY
342: + "' persistence mode. (Servers in this mode: "
343: + badServers
344: + ".) In a "
345: + "clustered DSO configuration, all servers must be in the '"
346: + PersistenceMode.PERMANENT_STORE
347: + "' mode. Please adjust the "
348: + "persistence/mode element (inside the 'server' element) in your "
349: + "configuration file; see the Terracotta documentation for more details.");
350: }
351: }
352: }
353:
354: private void validateLicenseModuleRestrictions()
355: throws ConfigurationSetupException {
356: Capabilities capabilities = AbstractCapabilitiesFactory
357: .getCapabilitiesManager();
358:
359: if (!capabilities.canClusterPOJOs()) {
360: Object result = this
361: .dsoApplicationConfigFor(
362: TVSConfigurationSetupManagerFactory.DEFAULT_APPLICATION_NAME)
363: .roots().getObject();
364: if (result != null && Array.getLength(result) > 0) {
365: // formatting
366: throw new ConfigurationSetupException(
367: "Your Terracotta license, "
368: + capabilities.describe()
369: + ", does not allow you to define DSO roots in your configuration file. Please remove them and try again.");
370: }
371: }
372:
373: }
374:
375: public NewCommonL2Config commonL2ConfigFor(String name)
376: throws ConfigurationSetupException {
377: return configDataFor(name).commonL2Config();
378: }
379:
380: public NewCommonL2Config commonl2Config() {
381: return this .myConfigData.commonL2Config();
382: }
383:
384: public NewSystemConfig systemConfig() {
385: return this .systemConfig;
386: }
387:
388: public NewL2DSOConfig dsoL2ConfigFor(String name)
389: throws ConfigurationSetupException {
390: return configDataFor(name).dsoL2Config();
391: }
392:
393: public NewL2DSOConfig dsoL2Config() {
394: return this .myConfigData.dsoL2Config();
395: }
396:
397: public NewHaConfig haConfig() {
398: return haConfig;
399: }
400:
401: public UpdateCheckConfig updateCheckConfig() {
402: return updateCheckConfig;
403: }
404:
405: public String[] allCurrentlyKnownServers() {
406: Servers serversBean = (Servers) serversBeanRepository().bean();
407: Server[] l2s = serversBean == null ? null : serversBean
408: .getServerArray();
409: if (l2s == null || l2s.length == 0)
410: return new String[] { null };
411: else {
412: String[] out = new String[l2s.length];
413: for (int i = 0; i < l2s.length; ++i)
414: out[i] = l2s[i].getName();
415: return out;
416: }
417: }
418:
419: public InputStream rawConfigFile() {
420: // This MUST piece together the configuration from our currently-active bean repositories. If we just read the
421: // actual config file we got on startup, we'd be sending out, well, the config we got on startup -- which might be
422: // quite different from our current config, if an L1 came in and overrode our config.
423:
424: TcConfigDocument doc = TcConfigDocument.Factory.newInstance();
425: TcConfigDocument.TcConfig config = doc.addNewTcConfig();
426:
427: System system = (System) this .systemBeanRepository().bean();
428: Client client = (Client) this .clientBeanRepository().bean();
429: Servers servers = (Servers) this .serversBeanRepository().bean();
430: Application application = (Application) this
431: .applicationsRepository()
432: .repositoryFor(
433: TVSConfigurationSetupManagerFactory.DEFAULT_APPLICATION_NAME)
434: .bean();
435:
436: if (system != null)
437: config.setSystem(system);
438: if (client != null)
439: config.setClients(client);
440: if (servers != null)
441: config.setServers(servers);
442: if (application != null)
443: config.setApplication(application);
444:
445: StringWriter sw = new StringWriter();
446: XmlOptions options = new XmlOptions().setSavePrettyPrint()
447: .setSavePrettyPrintIndent(4);
448:
449: try {
450: doc.save(sw, options);
451: } catch (IOException ioe) {
452: throw Assert.failure(
453: "Unexpected failure writing to in-memory streams",
454: ioe);
455: }
456:
457: String text = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n"
458: + sw.toString();
459:
460: try {
461: return new ByteArrayInputStream(text.getBytes("UTF-8"));
462: } catch (UnsupportedEncodingException uee) {
463: throw Assert.failure("This shouldn't be possible", uee);
464: }
465: }
466:
467: }
|