001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.config.schema.repository;
005:
006: import org.apache.commons.lang.ClassUtils;
007: import org.apache.xmlbeans.SchemaType;
008: import org.apache.xmlbeans.XmlException;
009: import org.apache.xmlbeans.XmlObject;
010: import org.apache.xmlbeans.XmlOptions;
011:
012: import com.tc.config.schema.listen.ConfigurationChangeListener;
013: import com.tc.config.schema.listen.ConfigurationChangeListenerSet;
014: import com.tc.config.schema.validate.ConfigurationValidator;
015: import com.tc.util.Assert;
016:
017: import java.lang.reflect.Field;
018: import java.lang.reflect.Modifier;
019: import java.util.ArrayList;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Set;
024:
025: /**
026: * The standard implementation of {@link MutableBeanRepository}.
027: */
028: public class StandardBeanRepository implements MutableBeanRepository {
029:
030: private final Class requiredClass;
031: private final ConfigurationChangeListenerSet listenerSet;
032: private final Set validators;
033: private XmlObject bean;
034:
035: private XmlObject preMutateCopy;
036:
037: public StandardBeanRepository(Class requiredClass) {
038: Assert.assertNotNull(requiredClass);
039:
040: this .requiredClass = requiredClass;
041: this .listenerSet = new ConfigurationChangeListenerSet();
042: this .validators = new HashSet();
043: this .bean = null;
044: }
045:
046: public void ensureBeanIsOfClass(Class theClass) {
047: if (!theClass.isAssignableFrom(this .requiredClass)) {
048: // formatting
049: throw Assert
050: .failure("You're making sure this repository requires at least "
051: + theClass
052: + ", but it requires "
053: + this .requiredClass
054: + ", which isn't that class or a subclass thereof.");
055: }
056: }
057:
058: public void saveCopyOfBeanInAnticipationOfFutureMutation() {
059: Assert.eval(this .preMutateCopy == null);
060: this .preMutateCopy = this .bean.copy();
061: }
062:
063: public void didMutateBean() {
064: Assert.eval(this .preMutateCopy != null);
065: this .listenerSet.configurationChanged(this .preMutateCopy,
066: this .bean);
067: this .preMutateCopy = null;
068: }
069:
070: public synchronized XmlObject bean() {
071: return this .bean;
072: }
073:
074: static SchemaType getTypeFieldFrom(Class theClass) {
075: try {
076: Field typeField = theClass.getField("type");
077:
078: Assert.eval(typeField.getType().equals(SchemaType.class));
079:
080: int modifiers = typeField.getModifiers();
081: Assert.eval(Modifier.isPublic(modifiers));
082: Assert.eval(Modifier.isStatic(modifiers));
083: Assert.eval(Modifier.isFinal(modifiers));
084:
085: return (SchemaType) typeField.get(null);
086: } catch (NoSuchFieldException nsfe) {
087: throw Assert
088: .failure(
089: "Class "
090: + theClass.getName()
091: + ", doesn't have a 'public static final SchemaType type' field?",
092: nsfe);
093: } catch (IllegalArgumentException iae) {
094: throw Assert.failure(
095: "Unable to get 'public static final SchemaType type' from class "
096: + theClass.getName(), iae);
097: } catch (IllegalAccessException iae) {
098: throw Assert.failure(
099: "Unable to get 'public static final SchemaType type' from class "
100: + theClass.getName(), iae);
101: }
102: }
103:
104: public SchemaType rootBeanSchemaType() {
105: return getTypeFieldFrom(this .requiredClass);
106: }
107:
108: public synchronized void setBean(XmlObject bean,
109: String sourceDescription) throws XmlException {
110: Assert.assertNotBlank(sourceDescription);
111: Assert
112: .eval(bean == null
113: || this .requiredClass.isInstance(bean));
114:
115: if (this .bean == bean)
116: return;
117:
118: if (bean != null) {
119: throwExceptionIfSchemaValidationFails(bean,
120: sourceDescription);
121:
122: Iterator iter = this .validators.iterator();
123: while (iter.hasNext()) {
124: ((ConfigurationValidator) iter.next()).validate(bean);
125: }
126: }
127:
128: XmlObject oldBean = this .bean;
129: this .bean = bean;
130: this .listenerSet.configurationChanged(oldBean, bean);
131: }
132:
133: private void throwExceptionIfSchemaValidationFails(
134: XmlObject theBean, String sourceDescription)
135: throws XmlException {
136: List errors = new ArrayList();
137: XmlOptions options = new XmlOptions();
138: options = options.setLoadLineNumbers();
139: options = options.setErrorListener(errors);
140: options = options.setDocumentSourceName(sourceDescription);
141:
142: boolean validated = theBean.validate(options);
143:
144: if (errors.size() > 0 || (!validated)) {
145: StringBuffer descrip = new StringBuffer();
146:
147: descrip.append("The configuration from '"
148: + sourceDescription + "' is invalid; it has "
149: + errors.size() + " error"
150: + (errors.size() == 1 ? "" : "s") + ":\n");
151:
152: int pos = 1;
153: Iterator iter = errors.iterator();
154: while (iter.hasNext()) {
155: descrip.append(" " + pos + ": "
156: + iter.next().toString() + "\n");
157: pos++;
158: }
159:
160: throw new XmlException(descrip.toString());
161: }
162: }
163:
164: public void addListener(ConfigurationChangeListener listener) {
165: Assert.assertNotNull(listener);
166: this .listenerSet.addListener(listener);
167: }
168:
169: public void addValidator(ConfigurationValidator validator) {
170: Assert.assertNotNull(validator);
171: this .validators.add(validator);
172: }
173:
174: public String toString() {
175: return "<Repository for bean of class "
176: + ClassUtils.getShortClassName(this .requiredClass)
177: + "; have bean? " + (this .bean != null) + ">";
178: }
179:
180: }
|