Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » beans » factory » xml » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.beans.factory.xml;
   18   
   19   import java.io.IOException;
   20   import java.io.InputStream;
   21   import java.util.HashSet;
   22   import java.util.Set;
   23   
   24   import javax.xml.parsers.ParserConfigurationException;
   25   
   26   import org.w3c.dom.Document;
   27   import org.xml.sax.EntityResolver;
   28   import org.xml.sax.ErrorHandler;
   29   import org.xml.sax.InputSource;
   30   import org.xml.sax.SAXException;
   31   import org.xml.sax.SAXParseException;
   32   
   33   import org.springframework.beans.BeanUtils;
   34   import org.springframework.beans.factory.BeanDefinitionStoreException;
   35   import org.springframework.beans.factory.parsing.EmptyReaderEventListener;
   36   import org.springframework.beans.factory.parsing.FailFastProblemReporter;
   37   import org.springframework.beans.factory.parsing.NullSourceExtractor;
   38   import org.springframework.beans.factory.parsing.ProblemReporter;
   39   import org.springframework.beans.factory.parsing.ReaderEventListener;
   40   import org.springframework.beans.factory.parsing.SourceExtractor;
   41   import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
   42   import org.springframework.beans.factory.support.BeanDefinitionRegistry;
   43   import org.springframework.core.Constants;
   44   import org.springframework.core.NamedThreadLocal;
   45   import org.springframework.core.io.DescriptiveResource;
   46   import org.springframework.core.io.Resource;
   47   import org.springframework.core.io.ResourceLoader;
   48   import org.springframework.core.io.support.EncodedResource;
   49   import org.springframework.util.Assert;
   50   import org.springframework.util.xml.SimpleSaxErrorHandler;
   51   import org.springframework.util.xml.XmlValidationModeDetector;
   52   
   53   /**
   54    * Bean definition reader for XML bean definitions.
   55    * Delegates the actual XML document reading to an implementation
   56    * of the {@link BeanDefinitionDocumentReader} interface.
   57    *
   58    * <p>Typically applied to a
   59    * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
   60    * or a {@link org.springframework.context.support.GenericApplicationContext}.
   61    *
   62    * <p>This class loads a DOM document and applies the BeanDefinitionDocumentReader to it.
   63    * The document reader will register each bean definition with the given bean factory,
   64    * talking to the latter's implementation of the
   65    * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} interface.
   66    *
   67    * @author Juergen Hoeller
   68    * @author Rob Harrop
   69    * @since 26.11.2003
   70    * @see #setDocumentReaderClass
   71    * @see BeanDefinitionDocumentReader
   72    * @see DefaultBeanDefinitionDocumentReader
   73    * @see BeanDefinitionRegistry
   74    * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
   75    * @see org.springframework.context.support.GenericApplicationContext
   76    */
   77   public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
   78   
   79   	/**
   80   	 * Indicates that the validation should be disabled.
   81   	 */
   82   	public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE;
   83   
   84   	/**
   85   	 * Indicates that the validation mode should be detected automatically.
   86   	 */
   87   	public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;
   88   
   89   	/**
   90   	 * Indicates that DTD validation should be used.
   91   	 */
   92   	public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;
   93   
   94   	/**
   95   	 * Indicates that XSD validation should be used.
   96   	 */
   97   	public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;
   98   
   99   
  100   	/** Constants instance for this class */
  101   	private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);
  102   
  103   	private boolean namespaceAware;
  104   
  105   	private int validationMode = VALIDATION_AUTO;
  106   
  107   	private Class parserClass;
  108   
  109   	private Class documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
  110   
  111   	private ProblemReporter problemReporter = new FailFastProblemReporter();
  112   
  113   	private ReaderEventListener eventListener = new EmptyReaderEventListener();
  114   
  115   	private SourceExtractor sourceExtractor = new NullSourceExtractor();
  116   
  117   	private NamespaceHandlerResolver namespaceHandlerResolver;
  118   
  119   	private DocumentLoader documentLoader = new DefaultDocumentLoader();
  120   
  121   	private EntityResolver entityResolver;
  122   
  123   	private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
  124   
  125   	private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
  126   
  127   	private final ThreadLocal resourcesCurrentlyBeingLoaded =
  128   			new NamedThreadLocal("XML bean definition resources currently being loaded");
  129   
  130   
  131   	/**
  132   	 * Create new XmlBeanDefinitionReader for the given bean factory.
  133   	 * @param registry the BeanFactory to load bean definitions into,
  134   	 * in the form of a BeanDefinitionRegistry
  135   	 */
  136   	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
  137   		super(registry);
  138   	}
  139   
  140   
  141   	/**
  142   	 * Set whether or not the XML parser should be XML namespace aware.
  143   	 * Default is "false".
  144   	 */
  145   	public void setNamespaceAware(boolean namespaceAware) {
  146   		this.namespaceAware = namespaceAware;
  147   	}
  148   
  149   	/**
  150   	 * Return whether or not the XML parser should be XML namespace aware.
  151   	 */
  152   	public boolean isNamespaceAware() {
  153   		return this.namespaceAware;
  154   	}
  155   
  156   	/**
  157   	 * Set if the XML parser should validate the document and thus enforce a DTD.
  158   	 * @deprecated as of Spring 2.0: superseded by "validationMode"
  159   	 * @see #setValidationMode
  160   	 */
  161   	public void setValidating(boolean validating) {
  162   		this.validationMode = (validating ? VALIDATION_AUTO : VALIDATION_NONE);
  163   	}
  164   
  165   	/**
  166   	 * Set the validation mode to use by name. Defaults to {@link #VALIDATION_AUTO}.
  167   	 */
  168   	public void setValidationModeName(String validationModeName) {
  169   		setValidationMode(constants.asNumber(validationModeName).intValue());
  170   	}
  171   
  172   	/**
  173   	 * Set the validation mode to use. Defaults to {@link #VALIDATION_AUTO}.
  174   	 */
  175   	public void setValidationMode(int validationMode) {
  176   		this.validationMode = validationMode;
  177   	}
  178   
  179   	/**
  180   	 * Return the validation mode to use.
  181   	 */
  182   	public int getValidationMode() {
  183   		return this.validationMode;
  184   	}
  185   
  186   	/**
  187   	 * Specify which {@link org.springframework.beans.factory.parsing.ProblemReporter} to use. Default implementation is
  188   	 * {@link org.springframework.beans.factory.parsing.FailFastProblemReporter} which exhibits fail fast behaviour. External tools
  189   	 * can provide an alternative implementation that collates errors and warnings for
  190   	 * display in the tool UI.
  191   	 */
  192   	public void setProblemReporter(ProblemReporter problemReporter) {
  193   		this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter());
  194   	}
  195   
  196   	/**
  197   	 * Specify which {@link ReaderEventListener} to use. Default implementation is
  198   	 * EmptyReaderEventListener which discards every event notification. External tools
  199   	 * can provide an alternative implementation to monitor the components being registered
  200   	 * in the BeanFactory.
  201   	 */
  202   	public void setEventListener(ReaderEventListener eventListener) {
  203   		this.eventListener = (eventListener != null ? eventListener : new EmptyReaderEventListener());
  204   	}
  205   
  206   	/**
  207   	 * Specify the {@link SourceExtractor} to use. The default implementation is
  208   	 * {@link NullSourceExtractor} which simply returns <code>null</code> as the source object.
  209   	 * This means that during normal runtime execution no additional source metadata is attached
  210   	 * to the bean configuration metadata.
  211   	 */
  212   	public void setSourceExtractor(SourceExtractor sourceExtractor) {
  213   		this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new NullSourceExtractor());
  214   	}
  215   
  216   	/**
  217   	 * Specify the {@link NamespaceHandlerResolver} to use. If none is specified a default
  218   	 * instance will be created by {@link #createDefaultNamespaceHandlerResolver()}.
  219   	 */
  220   	public void setNamespaceHandlerResolver(NamespaceHandlerResolver namespaceHandlerResolver) {
  221   		this.namespaceHandlerResolver = namespaceHandlerResolver;
  222   	}
  223   
  224   	/**
  225   	 * Specify the {@link DocumentLoader} to use. The default implementation is
  226   	 * {@link DefaultDocumentLoader} which loads {@link Document} instances using JAXP.
  227   	 */
  228   	public void setDocumentLoader(DocumentLoader documentLoader) {
  229   		this.documentLoader = (documentLoader != null ? documentLoader : new DefaultDocumentLoader());
  230   	}
  231   
  232   	/**
  233   	 * Set a SAX entity resolver to be used for parsing. By default,
  234   	 * BeansDtdResolver will be used. Can be overridden for custom entity
  235   	 * resolution, for example relative to some specific base path.
  236   	 * @see BeansDtdResolver
  237   	 */
  238   	public void setEntityResolver(EntityResolver entityResolver) {
  239   		this.entityResolver = entityResolver;
  240   	}
  241   
  242   	/**
  243   	 * Return the EntityResolver to use, building a default resolver
  244   	 * if none specified.
  245   	 */
  246   	protected EntityResolver getEntityResolver() {
  247   		if (this.entityResolver == null) {
  248   			// Determine default EntityResolver to use.
  249   			ResourceLoader resourceLoader = getResourceLoader();
  250   			if (resourceLoader != null) {
  251   				this.entityResolver = new ResourceEntityResolver(resourceLoader);
  252   			}
  253   			else {
  254   				this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
  255   			}
  256   		}
  257   		return this.entityResolver;
  258   	}
  259   
  260   	/**
  261   	 * Set an implementation of the <code>org.xml.sax.ErrorHandler</code>
  262   	 * interface for custom handling of XML parsing errors and warnings.
  263   	 * <p>If not set, a default SimpleSaxErrorHandler is used that simply
  264   	 * logs warnings using the logger instance of the view class,
  265   	 * and rethrows errors to discontinue the XML transformation.
  266   	 * @see SimpleSaxErrorHandler
  267   	 */
  268   	public void setErrorHandler(ErrorHandler errorHandler) {
  269   		this.errorHandler = errorHandler;
  270   	}
  271   
  272   	/**
  273   	 * Set the XmlBeanDefinitionParser implementation to use,
  274   	 * responsible for the actual parsing of XML bean definitions.
  275   	 * @deprecated as of Spring 2.0: superseded by "documentReaderClass"
  276   	 * @see #setDocumentReaderClass
  277   	 * @see XmlBeanDefinitionParser
  278   	 */
  279   	public void setParserClass(Class parserClass) {
  280   		if (this.parserClass == null || !XmlBeanDefinitionParser.class.isAssignableFrom(parserClass)) {
  281   			throw new IllegalArgumentException("'parserClass' must be an XmlBeanDefinitionParser");
  282   		}
  283   		this.parserClass = parserClass;
  284   	}
  285   
  286   	/**
  287   	 * Specify the BeanDefinitionDocumentReader implementation to use,
  288   	 * responsible for the actual reading of the XML bean definition document.
  289   	 * <p>Default is DefaultBeanDefinitionDocumentReader.
  290   	 * @param documentReaderClass the desired BeanDefinitionDocumentReader implementation class
  291   	 * @see BeanDefinitionDocumentReader
  292   	 * @see DefaultBeanDefinitionDocumentReader
  293   	 */
  294   	public void setDocumentReaderClass(Class documentReaderClass) {
  295   		if (documentReaderClass == null || !BeanDefinitionDocumentReader.class.isAssignableFrom(documentReaderClass)) {
  296   			throw new IllegalArgumentException(
  297   					"documentReaderClass must be an implementation of the BeanDefinitionDocumentReader interface");
  298   		}
  299   		this.documentReaderClass = documentReaderClass;
  300   	}
  301   
  302   
  303   	/**
  304   	 * Load bean definitions from the specified XML file.
  305   	 * @param resource the resource descriptor for the XML file
  306   	 * @return the number of bean definitions found
  307   	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
  308   	 */
  309   	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  310   		return loadBeanDefinitions(new EncodedResource(resource));
  311   	}
  312   
  313   	/**
  314   	 * Load bean definitions from the specified XML file.
  315   	 * @param encodedResource the resource descriptor for the XML file,
  316   	 * allowing to specify an encoding to use for parsing the file
  317   	 * @return the number of bean definitions found
  318   	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
  319   	 */
  320   	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  321   		Assert.notNull(encodedResource, "EncodedResource must not be null");
  322   		if (logger.isInfoEnabled()) {
  323   			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  324   		}
  325   
  326   		Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();
  327   		if (currentResources == null) {
  328   			currentResources = new HashSet(4);
  329   			this.resourcesCurrentlyBeingLoaded.set(currentResources);
  330   		}
  331   		if (!currentResources.add(encodedResource)) {
  332   			throw new BeanDefinitionStoreException(
  333   					"Detected recursive loading of " + encodedResource + " - check your import definitions!");
  334   		}
  335   		try {
  336   			InputStream inputStream = encodedResource.getResource().getInputStream();
  337   			try {
  338   				InputSource inputSource = new InputSource(inputStream);
  339   				if (encodedResource.getEncoding() != null) {
  340   					inputSource.setEncoding(encodedResource.getEncoding());
  341   				}
  342   				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  343   			}
  344   			finally {
  345   				inputStream.close();
  346   			}
  347   		}
  348   		catch (IOException ex) {
  349   			throw new BeanDefinitionStoreException(
  350   					"IOException parsing XML document from " + encodedResource.getResource(), ex);
  351   		}
  352   		finally {
  353   			currentResources.remove(encodedResource);
  354   			if (currentResources.isEmpty()) {
  355   				this.resourcesCurrentlyBeingLoaded.set(null);
  356   			}
  357   		}
  358   	}
  359   
  360   	/**
  361   	 * Load bean definitions from the specified XML file.
  362   	 * @param inputSource the SAX InputSource to read from
  363   	 * @return the number of bean definitions found
  364   	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
  365   	 */
  366   	public int loadBeanDefinitions(InputSource inputSource) throws BeanDefinitionStoreException {
  367   		return loadBeanDefinitions(inputSource, "resource loaded through SAX InputSource");
  368   	}
  369   
  370   	/**
  371   	 * Load bean definitions from the specified XML file.
  372   	 * @param inputSource the SAX InputSource to read from
  373   	 * @param resourceDescription a description of the resource
  374   	 * (can be <code>null</code> or empty)
  375   	 * @return the number of bean definitions found
  376   	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
  377   	 */
  378   	public int loadBeanDefinitions(InputSource inputSource, String resourceDescription)
  379   			throws BeanDefinitionStoreException {
  380   
  381   		return doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription));
  382   	}
  383   
  384   
  385   	/**
  386   	 * Actually load bean definitions from the specified XML file.
  387   	 * @param inputSource the SAX InputSource to read from
  388   	 * @param resource the resource descriptor for the XML file
  389   	 * @return the number of bean definitions found
  390   	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
  391   	 */
  392   	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  393   			throws BeanDefinitionStoreException {
  394   		try {
  395   			int validationMode = getValidationModeForResource(resource);
  396   			Document doc = this.documentLoader.loadDocument(
  397   					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
  398   			return registerBeanDefinitions(doc, resource);
  399   		}
  400   		catch (BeanDefinitionStoreException ex) {
  401   			throw ex;
  402   		}
  403   		catch (SAXParseException ex) {
  404   			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  405   					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  406   		}
  407   		catch (SAXException ex) {
  408   			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  409   					"XML document from " + resource + " is invalid", ex);
  410   		}
  411   		catch (ParserConfigurationException ex) {
  412   			throw new BeanDefinitionStoreException(resource.getDescription(),
  413   					"Parser configuration exception parsing XML from " + resource, ex);
  414   		}
  415   		catch (IOException ex) {
  416   			throw new BeanDefinitionStoreException(resource.getDescription(),
  417   					"IOException parsing XML document from " + resource, ex);
  418   		}
  419   		catch (Throwable ex) {
  420   			throw new BeanDefinitionStoreException(resource.getDescription(),
  421   					"Unexpected exception parsing XML document from " + resource, ex);
  422   		}
  423   	}
  424   
  425   
  426   	/**
  427   	 * Gets the validation mode for the specified {@link Resource}. If no explicit
  428   	 * validation mode has been configured then the validation mode is
  429   	 * {@link #detectValidationMode detected}.
  430   	 * <p>Override this method if you would like full control over the validation
  431   	 * mode, even when something other than {@link #VALIDATION_AUTO} was set.
  432   	 */
  433   	protected int getValidationModeForResource(Resource resource) {
  434   		int validationModeToUse = getValidationMode();
  435   		if (validationModeToUse != VALIDATION_AUTO) {
  436   			return validationModeToUse;
  437   		}
  438   		int detectedMode = detectValidationMode(resource);
  439   		if (detectedMode != VALIDATION_AUTO) {
  440   			return detectedMode;
  441   		}
  442   		// Hmm, we didn't get a clear indication... Let's assume XSD,
  443   		// since apparently no DTD declaration has been found up until
  444   		// detection stopped (before finding the document's root tag).
  445   		return VALIDATION_XSD;
  446   	}
  447   
  448   	/**
  449   	 * Detects which kind of validation to perform on the XML file identified
  450   	 * by the supplied {@link Resource}. If the file has a <code>DOCTYPE</code>
  451   	 * definition then DTD validation is used otherwise XSD validation is assumed.
  452   	 * <p>Override this method if you would like to customize resolution
  453   	 * of the {@link #VALIDATION_AUTO} mode.
  454   	 */
  455   	protected int detectValidationMode(Resource resource) {
  456   		if (resource.isOpen()) {
  457   			throw new BeanDefinitionStoreException(
  458   					"Passed-in Resource [" + resource + "] contains an open stream: " +
  459   					"cannot determine validation mode automatically. Either pass in a Resource " +
  460   					"that is able to create fresh streams, or explicitly specify the validationMode " +
  461   					"on your XmlBeanDefinitionReader instance.");
  462   		}
  463   
  464   		InputStream inputStream;
  465   		try {
  466   			inputStream = resource.getInputStream();
  467   		}
  468   		catch (IOException ex) {
  469   			throw new BeanDefinitionStoreException(
  470   					"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
  471   					"Did you attempt to load directly from a SAX InputSource without specifying the " +
  472   					"validationMode on your XmlBeanDefinitionReader instance?", ex);
  473   		}
  474   
  475   		try {
  476   			return this.validationModeDetector.detectValidationMode(inputStream);
  477   		}
  478   		catch (IOException ex) {
  479   			throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
  480   					resource + "]: an error occurred whilst reading from the InputStream.", ex);
  481   		}
  482   	}
  483   
  484   	/**
  485   	 * Register the bean definitions contained in the given DOM document.
  486   	 * Called by <code>loadBeanDefinitions</code>.
  487   	 * <p>Creates a new instance of the parser class and invokes
  488   	 * <code>registerBeanDefinitions</code> on it.
  489   	 * @param doc the DOM document
  490   	 * @param resource the resource descriptor (for context information)
  491   	 * @return the number of bean definitions found
  492   	 * @throws BeanDefinitionStoreException in case of parsing errors
  493   	 * @see #loadBeanDefinitions
  494   	 * @see #setDocumentReaderClass
  495   	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
  496   	 */
  497   	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  498   		// Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
  499   		if (this.parserClass != null) {
  500   			XmlBeanDefinitionParser parser =
  501   					(XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
  502   			return parser.registerBeanDefinitions(this, doc, resource);
  503   		}
  504   		// Read document based on new BeanDefinitionDocumentReader SPI.
  505   		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  506   		int countBefore = getRegistry().getBeanDefinitionCount();
  507   		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  508   		return getRegistry().getBeanDefinitionCount() - countBefore;
  509   	}
  510   
  511   	/**
  512   	 * Create the {@link BeanDefinitionDocumentReader} to use for actually
  513   	 * reading bean definitions from an XML document.
  514   	 * <p>Default implementation instantiates the specified "documentReaderClass".
  515   	 * @see #setDocumentReaderClass
  516   	 */
  517   	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
  518   		return (BeanDefinitionDocumentReader) BeanUtils.instantiateClass(this.documentReaderClass);
  519   	}
  520   
  521   	/**
  522   	 * Create the {@link XmlReaderContext} to pass over to the document reader.
  523   	 */
  524   	protected XmlReaderContext createReaderContext(Resource resource) {
  525   		if (this.namespaceHandlerResolver == null) {
  526   			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
  527   		}
  528   		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
  529   				this.sourceExtractor, this, this.namespaceHandlerResolver);
  530   	}
  531   
  532   	/**
  533   	 * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
  534   	 * Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
  535   	 */
  536   	protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
  537   		return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
  538   	}
  539   
  540   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » beans » factory » xml » [javadoc | source]