Package org.springframework.ide.eclipse.beans.core.internal.model

Source Code of org.springframework.ide.eclipse.beans.core.internal.model.BeansConfig

/*******************************************************************************
* Copyright (c) 2004, 2014 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.beans.core.internal.model;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.ui.IPersistableElement;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.AliasDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.parsing.ComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.parsing.DefaultsDefinition;
import org.springframework.beans.factory.parsing.EmptyReaderEventListener;
import org.springframework.beans.factory.parsing.ImportDefinition;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.ReaderEventListener;
import org.springframework.beans.factory.parsing.SourceExtractor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.beans.factory.xml.BeanDefinitionDocumentReader;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
import org.springframework.beans.factory.xml.DocumentDefaultsDefinition;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerResolver;
import org.springframework.beans.factory.xml.PluggableSchemaResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.internal.model.namespaces.DelegatingNamespaceHandlerResolver;
import org.springframework.ide.eclipse.beans.core.internal.model.namespaces.DocumentAccessor;
import org.springframework.ide.eclipse.beans.core.internal.model.namespaces.XmlCatalogDelegatingEntityResolver;
import org.springframework.ide.eclipse.beans.core.internal.model.process.BeansConfigPostProcessorFactory;
import org.springframework.ide.eclipse.beans.core.internal.parser.BeansDtdResolver;
import org.springframework.ide.eclipse.beans.core.model.IBean;
import org.springframework.ide.eclipse.beans.core.model.IBeansComponent;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfigEventListener;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet;
import org.springframework.ide.eclipse.beans.core.model.IBeansImport;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.beans.core.model.IReloadableBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.process.IBeansConfigPostProcessor;
import org.springframework.ide.eclipse.beans.core.namespaces.IModelElementProvider;
import org.springframework.ide.eclipse.beans.core.namespaces.NamespaceUtils;
import org.springframework.ide.eclipse.core.SpringCore;
import org.springframework.ide.eclipse.core.SpringCoreUtils;
import org.springframework.ide.eclipse.core.io.EclipsePathMatchingResourcePatternResolver;
import org.springframework.ide.eclipse.core.io.ExternalFile;
import org.springframework.ide.eclipse.core.io.FileResource;
import org.springframework.ide.eclipse.core.io.StorageResource;
import org.springframework.ide.eclipse.core.io.ZipEntryStorage;
import org.springframework.ide.eclipse.core.io.xml.LineNumberPreservingDOMParser;
import org.springframework.ide.eclipse.core.io.xml.XercesDocumentLoader;
import org.springframework.ide.eclipse.core.java.JdtUtils;
import org.springframework.ide.eclipse.core.model.DefaultModelSourceLocation;
import org.springframework.ide.eclipse.core.model.ILazyInitializedModelElement;
import org.springframework.ide.eclipse.core.model.IModelElement;
import org.springframework.ide.eclipse.core.model.IModelSourceLocation;
import org.springframework.ide.eclipse.core.model.IResourceModelElement;
import org.springframework.ide.eclipse.core.model.ISourceModelElement;
import org.springframework.ide.eclipse.core.model.validation.ValidationProblem;
import org.springframework.ide.eclipse.core.model.xml.XmlSourceLocation;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
* This class defines a Spring beans configuration.
* @author Torsten Juergeleit
* @author Dave Watkins
* @author Christian Dupuis
* @author Martin Lippert
*/
public class BeansConfig extends AbstractBeansConfig implements IBeansConfig, ILazyInitializedModelElement, IReloadableBeansConfig {

  private static final String DEBUG_OPTION = BeansCorePlugin.PLUGIN_ID + "/model/loading/debug";

  private static final boolean DEBUG = SpringCore.isDebug(DEBUG_OPTION);

  /** The default element provider used for non-namespaced elements */
  public static final IModelElementProvider DEFAULT_ELEMENT_PROVIDER = new DefaultModelElementProvider();

  /** The resource that is currently being processed or null if non is processed */
  private volatile IResource currentResource = null;

  /** The resource that is currently being processed or null if non is processed; just a different type then the above */
  private volatile EncodedResource currentEncodedResource;

  /** {@link IBeansConfigPostProcessor}s that are detected in this configs beans and component map */
  private volatile Set<IBeansConfigPostProcessor> ownPostProcessors = new HashSet<IBeansConfigPostProcessor>();

  /** {@link IBeansConfigPostProcessor}s that have been removed between the two last reads of the backing xml file */
  private volatile Set<IBeansConfigPostProcessor> removedPostProcessors = new HashSet<IBeansConfigPostProcessor>();

  /**
   * {@link IBeansConfigPostProcessor}s that have been contributed by external {@link IBeansConfig} from other
   * {@link IBeansConfigSet}
   */
  private volatile Map<IBeansConfigPostProcessor, Set<IBeansConfig>> externalPostProcessors = new ConcurrentHashMap<IBeansConfigPostProcessor, Set<IBeansConfig>>();

  /** {@link Resource} implementation to be passed to Spring core for reading */
  private volatile Resource resource;

  /** {@link ProblemReporter} implementation for later use */
  private volatile ProblemReporter problemReporter;

  /** {@link BeanNameGenerator} implementation for later use */
  private volatile BeanNameGenerator beanNameGenerator;

  /** {@link BeanDefinitionRegistry} implementation for later use */
  private volatile SimpleBeanDefinitionRegistry registry;

  /** Internal cache for all children */
  private transient IModelElement[] children;
 
  private transient Stack<CompositeComponentDefinition> componentDefinitions = new Stack<CompositeComponentDefinition>();

  /**
   * Creates a new {@link BeansConfig}.
   */
  public BeansConfig(IBeansProject project, String name, Type type) {
    super(project, name, type);
    init(name, project);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public IModelElement[] getElementChildren() {
    // Lazily initialization of this config
    readConfig();

    try {
      r.lock();
      return children;
    }
    finally {
      r.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean isInitialized() {
    return isModelPopulated;
  }

  /**
   * Sets internal list of {@link IBean}s to <code>null</code>. Any further access to the data of this instance of
   * {@link IBeansConfig} leads to reloading of the corresponding beans config file.
   */
  public void reload() {
    if (file != null) {
      try {
        w.lock();
        // System.out.println(String.format("++- resetting config '%s'", file.getFullPath().toString()));
        isModelPopulated = false;
        modificationTimestamp = IResource.NULL_STAMP;
        defaults = null;
        imports.clear();
        aliases.clear();
        beans.clear();
        components.clear();
        isBeanClassesMapPopulated = false;
        beanClassesMap.clear();
        problems.clear();
        children = null;
       
        componentDefinitions.clear();

      }
      finally {
        w.unlock();
      }

      // Reset all config sets which contain this config
      for (IBeansConfigEventListener eventListener : eventListeners) {
        eventListener.onReset(this);
      }
    }
  }

  /**
   * Checks the file for the given name. If the given name defines an external resource (leading '/' -> not part of
   * the project this config belongs to) get the file from the workspace else from the project. If the name specifies
   * an entry in an archive then the {@link #isArchived} flag is set. If the corresponding file is not available or
   * accessible then an entry is added to the config's list of errors.
   * @param project
   */
  protected void init(String name, IBeansProject project) {
    IContainer container = null;
    String fileName = null;
    String fullPath = null;

    // At first check for a config file in a JAR
    int pos = name.indexOf(ZipEntryStorage.DELIMITER);
    if (pos != -1) {
      isArchived = true;
      fileName = name.substring(0, pos);
    }
    else {
      fileName = name;
    }

    // Now check if is an workspace external resource
    if (fileName.startsWith(EXTERNAL_FILE_NAME_PREFIX)) {

      fileName = fileName.substring(EXTERNAL_FILE_NAME_PREFIX.length());

      // Resolve eventual contained classpath variables
      IPath resolvedPath = JavaCore.getResolvedVariablePath(new Path(fileName));
      if (resolvedPath != null) {
        fileName = resolvedPath.toString();
      }

      // Create an external file instance
      file = new ExternalFile(new File(fileName), name.substring(pos + 1), project.getProject());
    }
    else {
      container = (IProject) ((IResourceModelElement) getElementParent()).getElementResource();
      fullPath = container.getFullPath().append(fileName).toString();

      // Try to find the configuration file in the workspace
      file = (IFile) container.findMember(fileName);
    }

    if (file == null || !file.exists()) {
      modificationTimestamp = IResource.NULL_STAMP;
      String msg = "Beans config file '" + fullPath + "' not accessible";
      problems = new CopyOnWriteArraySet<ValidationProblem>();
      problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, msg, file, -1));
    }
    else {
      modificationTimestamp = file.getModificationStamp();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void readConfig() {
    if (!this.isModelPopulated) {

      long start = System.currentTimeMillis();
      long count = 0;
     
      w.lock();
      if (this.isModelPopulated) {
        w.unlock();
        return;
      }
     
      final ClassLoader projectIncludingClassloader = getProjectRelatedClassLoader();

      try {
        // Publish start events
        for (IBeansConfigEventListener eventListener : eventListeners) {
          eventListener.onReadStart(this);
        }

        if (file != null && file.exists()) {
         
          // Only install Eclipse-based resource loader if enabled in project properties
          // IMPORTANT: the following block needs to stay before the w.lock()
          // as it could otherwise create a runtime deadlock
          final ResourceLoader resourceLoader;
          if (getElementParent() instanceof IBeansProject && ((IBeansProject) getElementParent()).isImportsEnabled()) {
            resourceLoader = new EclipsePathMatchingResourcePatternResolver(file.getProject(), projectIncludingClassloader);
          }
          else {
            resourceLoader = new ClassResourceFilteringPatternResolver(file.getProject(), projectIncludingClassloader);
          }

          modificationTimestamp = file.getModificationStamp();
          if (isArchived) {
            if (file instanceof Resource) {
              resource = (Resource) file;
            }
            else {
              resource = new StorageResource(new ZipEntryStorage(file.getProject(), getElementName()),
                  file.getProject());
            }
          }
          else {
            resource = new FileResource(file);
          }

          // Set up classloader to use for NamespaceHandler and XSD loading
          ClassLoader namespaceResolvingClassloader = projectIncludingClassloader;
          if (!NamespaceUtils.useNamespacesFromClasspath(file.getProject())) {
             namespaceResolvingClassloader = BeansCorePlugin.getClassLoader();
          }

          registry = new ScannedGenericBeanDefinitionSuppressingBeanDefinitionRegistry();
          EntityResolver resolver = new XmlCatalogDelegatingEntityResolver(new BeansDtdResolver(), new PluggableSchemaResolver(namespaceResolvingClassloader));
          final DocumentAccessor documentAccessor = new DocumentAccessor();
          final SourceExtractor sourceExtractor = new DelegatingSourceExtractor(file.getProject());
          final BeansConfigReaderEventListener eventListener = new BeansConfigReaderEventListener(this, resource, sourceExtractor, documentAccessor);
          final NamespaceHandlerResolver namespaceHandlerResolver = new DelegatingNamespaceHandlerResolver(namespaceResolvingClassloader, this,  documentAccessor);
         
          problemReporter = new BeansConfigProblemReporter();
          beanNameGenerator = new UniqueBeanNameGenerator(this);

          final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry) {

            @Override
            public int loadBeanDefinitions(EncodedResource encodedResource)
                throws BeanDefinitionStoreException {

              // Capture the current resource being processed to handle parsing exceptions correctly and
              // create the validation error on the correct resource
              if (encodedResource != null && encodedResource.getResource() instanceof IAdaptable) {
                currentResource = (IResource) ((IAdaptable) encodedResource.getResource()).getAdapter(IResource.class);
                currentEncodedResource = encodedResource;
              }

              try {
                // Delegate actual processing to XmlBeanDefinitionReader
                int loadedBeans = 0;
                if (encodedResource.getResource().exists()) {
                  loadedBeans = super.loadBeanDefinitions(encodedResource);
                }
                return loadedBeans;
              }
              finally {
                // Reset currently processed resource before leaving
                currentResource = null;
                currentEncodedResource = null;
              }
            }

            @Override
            public int registerBeanDefinitions(Document doc, Resource resource)
                throws BeanDefinitionStoreException {
              try {
                documentAccessor.pushDocument(doc);
                return super.registerBeanDefinitions(doc, resource);
              }
              finally {
                documentAccessor.popDocument();
              }
            }
           
            @Override
            public XmlReaderContext createReaderContext(Resource resource) {
              return new ProfileAwareReaderContext(resource, problemReporter, eventListener,
                  sourceExtractor, this, namespaceHandlerResolver);
            }
           
            @Override
            protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
              return new ToolingFriendlyBeanDefinitionDocumentReader(BeansConfig.this);
            }
          };

          reader.setDocumentLoader(new XercesDocumentLoader());
          reader.setResourceLoader(resourceLoader);

          reader.setEntityResolver(resolver);
          reader.setSourceExtractor(sourceExtractor);
          reader.setEventListener(eventListener);
          reader.setProblemReporter(problemReporter);
          reader.setErrorHandler(new BeansConfigErrorHandler());
          reader.setNamespaceHandlerResolver(namespaceHandlerResolver);
          reader.setBeanNameGenerator(beanNameGenerator);
          reader.setEnvironment(new ToolingAwareEnvironment());
         
          final Map<Throwable, Integer> throwables = new HashMap<Throwable, Integer>();
          try {
            Callable<Integer> loadBeanDefinitionOperation = new Callable<Integer>() {

              public Integer call() {
               
                // Obtain thread context classloader and override with the project classloader
                ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(resourceLoader.getClassLoader());
         
                try {
                  // Load bean definitions
                  int count = reader.loadBeanDefinitions(resource);

                  // Finally register post processed beans and components
                  eventListener.registerComponents();

                  // Post process beans config if required
                  postProcess(resourceLoader.getClassLoader());

                  return count;
                }
                catch (Exception e) {
                  // Record the exception to throw it later
                  throwables.put(e, LineNumberPreservingDOMParser.getStartLineNumber(documentAccessor.getLastElement()));
                }
                finally {
                  // Reset the context classloader
                  Thread.currentThread().setContextClassLoader(threadClassLoader);
                }
                return 0;
              }

            };

            try {
              FutureTask<Integer> task = new FutureTask<Integer>(loadBeanDefinitionOperation);
              BeansCorePlugin.getExecutorService().submit(task);
              count = task.get(BeansCorePlugin.getDefault().getPreferenceStore().getInt(BeansCorePlugin.TIMEOUT_CONFIG_LOADING_PREFERENCE_ID),
                  TimeUnit.SECONDS);

              // if we recored an exception use this instead of stupid concurrent exception
              if (throwables.size() > 0) {
                throw throwables.keySet().iterator().next();
              }
            }
            catch (TimeoutException e) {
              problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, "Loading of resource '"
                  + resource.getFile().getAbsolutePath() + "' took more than "
                  + BeansCorePlugin.getDefault().getPreferenceStore()
                      .getInt(BeansCorePlugin.TIMEOUT_CONFIG_LOADING_PREFERENCE_ID) + "sec",
                  file, 1));
            }
          }
          catch (Throwable e) {
            int line = -1;
            if (throwables.containsKey(e)) {
              line = throwables.get(e);
            }
            // Skip SAXParseExceptions because they're already handled by the SAX ErrorHandler
            if (e instanceof BeanDefinitionStoreException) {
              if (e.getCause() != null) {
                problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, String.format(
                    "Error occured processing XML '%s'. See Error Log for more details", e.getCause().getMessage()), file, line));
                BeansCorePlugin.log(new Status(IStatus.INFO, BeansCorePlugin.PLUGIN_ID, String.format(
                    "Error occured processing '%s'", file.getFullPath()), e.getCause()));
              }
              else {
                problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, e.getMessage(), file, line));
                BeansCorePlugin.log(new Status(IStatus.INFO, BeansCorePlugin.PLUGIN_ID, String.format(
                    "Error occured processing '%s'", file.getFullPath()), e));
              }
            }
            else if (!(e.getCause() instanceof SAXParseException)
                && !(e instanceof BeanDefinitionParsingException)) {
              problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, e.getMessage(), file, line));
              BeansCorePlugin.log(new Status(IStatus.INFO, BeansCorePlugin.PLUGIN_ID, String.format(
                  "Error occured processing '%s'", file.getFullPath()), e));
            }
          }
        }
      }
      finally {

        // Prepare the internal cache of all children for faster access
        List<ISourceModelElement> allChildren = new ArrayList<ISourceModelElement>(imports);
        allChildren.addAll(aliases.values());
        allChildren.addAll(components);
        allChildren.addAll(beans.values());
        Collections.sort(allChildren, new Comparator<ISourceModelElement>() {
          public int compare(ISourceModelElement element1, ISourceModelElement element2) {
            return element1.getElementStartLine() - element2.getElementStartLine();
          }
        });
        this.children = allChildren.toArray(new IModelElement[allChildren.size()]);

        this.isModelPopulated = true;
        w.unlock();

        // Run external post processors
        postProcessExternal(externalPostProcessors.keySet(), projectIncludingClassloader);

        // Publish events for all existing post processors
        if (this.ownPostProcessors != null) {
          for (IBeansConfigPostProcessor postProcessor : ownPostProcessors) {
            for (IBeansConfigEventListener eventListener : eventListeners) {
              eventListener.onPostProcessorDetected(this, postProcessor);
            }
          }
        }
        // Publish events for all removed post processors
        if (this.removedPostProcessors != null) {
          for (IBeansConfigPostProcessor postProcessor : removedPostProcessors) {
            for (IBeansConfigEventListener eventListener : eventListeners) {
              eventListener.onPostProcessorRemoved(this, postProcessor);
            }
          }
        }

        for (IBeansConfigEventListener eventListener : eventListeners) {
          eventListener.onReadEnd(this);
        }

        if (DEBUG) {
          System.out.println(String.format("> loading of %s beans from %s took %sms", count, file
              .getFullPath().toString(), (System.currentTimeMillis() - start)));
        }
      }
    }
  }

  public ClassLoader getProjectRelatedClassLoader() {
    if (file != null && file.exists()) {
      return JdtUtils.getClassLoader(file.getProject(), BeansCorePlugin.getClassLoader());
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getAdapter(Class adapter) {
    if (adapter == IPersistableElement.class) {
      return new BeansModelElementToPersistableElementAdapter(this);
    }
    else if (adapter == IResource.class) {
      return getElementResource();
    }
    else if (adapter == Resource.class) {
      return resource;
    }
    return super.getAdapter(adapter);
  }

  /**
   * Entry into processing the contributed {@link IBeansConfigPostProcessor}.
   */
  private void postProcess(ClassLoader classloader) {

    Set<IBeansConfigPostProcessor> detectedPostProcessors = new LinkedHashSet<IBeansConfigPostProcessor>();
    removedPostProcessors = new LinkedHashSet<IBeansConfigPostProcessor>();

    // Create special ReaderEventListener that essentially just passes through component definitions
    ReaderEventListener eventListener = new BeansConfigPostProcessorReaderEventListener();

    // Collect the beans from this config and all imported configs
    List<IBean> beansClone = new ArrayList<IBean>();

    // Important: don't use getBeans or getComponents on this instance -> will lock
    beansClone.addAll(beans.values());
    for (IBeansComponent component : components) {
      addBeansFromCompoent(component, beansClone);
    }

    // Now collect from all imported configurations as well
    for (IBeansImport beansImport : imports) {
      for (IBeansConfig bc : beansImport.getImportedBeansConfigs()) {
        beansClone.addAll(bc.getBeans());
        for (IBeansComponent component : bc.getComponents()) {
          addBeansFromCompoent(component, beansClone);
        }
      }
    }

    // Run all generally contributed post processors
    for (IBeansConfigPostProcessor postProcessor : BeansConfigPostProcessorFactory.createPostProcessor(null)) {
      executePostProcessor(postProcessor, eventListener, classloader);
    }

    // Run all post processors specific to a bean class
    for (IBean bean : beansClone) {
      String beanClassName = bean.getClassName();
      if (beanClassName != null) {
        IType type = JdtUtils.getJavaType(getElementResource().getProject(), beanClassName);
        if (type != null) {
          for (IBeansConfigPostProcessor postProcessor : BeansConfigPostProcessorFactory
              .createPostProcessor(beanClassName)) {

            executePostProcessor(postProcessor, eventListener, classloader);

            // Keep the detected post processor for later
            detectedPostProcessors.add(postProcessor);
          }
        }
      }
    }

    // Detect deleted post processors
    for (IBeansConfigPostProcessor postProcessor : ownPostProcessors) {
      if (!detectedPostProcessors.contains(postProcessor)) {
        ownPostProcessors.remove(postProcessor);
        removedPostProcessors.add(postProcessor);
      }
    }

    // Detect newly added post processors
    for (IBeansConfigPostProcessor postProcessor : detectedPostProcessors) {
      if (!ownPostProcessors.contains(postProcessor)) {
        ownPostProcessors.add(postProcessor);
      }
    }

  }

  private void addBeansFromCompoent(IBeansComponent component, List<IBean> beansClone) {
    beansClone.addAll(component.getBeans());
   
    for (IBeansComponent bc : component.getComponents()) {
      addBeansFromCompoent(bc, beansClone);
    }
  }

  /**
   * Execute the externally added {@link IBeansConfigPostProcessor}s.
   * <p>
   * This will only execute the given post processors if this config is already populated.
   */
  private void postProcessExternal(Set<IBeansConfigPostProcessor> postProcessors, ClassLoader classloader) {
    if (this.isModelPopulated) {
      try {
        w.lock();

        // Create special ReaderEventListener that essentially just passes through component definitions
        ReaderEventListener eventListener = new BeansConfigPostProcessorReaderEventListener();

        // Run all external found post processor instances
        for (IBeansConfigPostProcessor postProcessor : postProcessors) {
          if (!ownPostProcessors.contains(postProcessor)) {
            executePostProcessor(postProcessor, eventListener, classloader);
          }
        }
      }
      finally {
        w.unlock();
      }
    }
  }

  /**
   * Add the given {@link IBeansConfigPostProcessor}.
   * <p>
   * If this config has already been populated only the given {@link IBeansConfigPostProcessor} will be executed;
   * otherwise executing is deferred until the model gets populated
   * @param classloader
   */
  protected void addExternalPostProcessor(IBeansConfigPostProcessor postProcessor, IBeansConfig config) {
    try {
      w.lock();
      if (externalPostProcessors.containsKey(postProcessor)) {
        externalPostProcessors.get(postProcessor).add(config);
      }
      else {
        Set<IBeansConfig> configs = new LinkedHashSet<IBeansConfig>();
        configs.add(config);
        externalPostProcessors.put(postProcessor, configs);

        if (!ownPostProcessors.contains(postProcessor)) {
          // Run the external post processors
          Set<IBeansConfigPostProcessor> postProcessors = new HashSet<IBeansConfigPostProcessor>();
          postProcessors.add(postProcessor);
          postProcessExternal(postProcessors, getProjectRelatedClassLoader());
        }
      }
    }
    finally {
      w.unlock();
    }
  }

  /**
   * Remove the given {@link IBeansConfigPostProcessor}.
   * <p>
   * This will trigger a reload of this config in order to remove obsolete {@link IBean}s.
   */
  protected void removeExternalPostProcessor(IBeansConfigPostProcessor postProcessor, IBeansConfig config) {
    try {
      w.lock();
      if (externalPostProcessors.containsKey(postProcessor)
          && externalPostProcessors.get(postProcessor).remove(config)) {
        if (externalPostProcessors.get(postProcessor).size() == 0) {
          externalPostProcessors.remove(postProcessor);
        }
      }
      reload();
    }
    finally {
      w.unlock();
    }
  }

  /**
   * Safely execute the given {@link IBeansConfigPostProcessor}.
   */
  private void executePostProcessor(final IBeansConfigPostProcessor postProcessor,
      final ReaderEventListener eventListener, final ClassLoader classloader) {
    SafeRunner.run(new ISafeRunnable() {

      public void handleException(Throwable exception) {
        BeansCorePlugin.log(exception);
      }

      public void run() throws Exception {
        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
       
        if (classloader != null) {
          Thread.currentThread().setContextClassLoader(classloader);
        }
       
        try {
          postProcessor.postProcess(BeansConfigPostProcessorFactory.createPostProcessingContext(BeansConfig.this,
              beans.values(), eventListener, problemReporter, beanNameGenerator, registry, problems));
        }
        finally {
          Thread.currentThread().setContextClassLoader(contextLoader);
        }
      }
    });
  }

  /**
   * Registers the given component definition with this {@link BeansConfig}'s beans and component storage.
   */
  private void registerComponentDefinition(ComponentDefinition componentDefinition,
      Map<String, IModelElementProvider> elementProviders) {
    String uri = NamespaceUtils.getNameSpaceURI(componentDefinition);
    IModelElementProvider provider = elementProviders.get(uri);
    if (provider == null) {
      provider = DEFAULT_ELEMENT_PROVIDER;
    }
    ISourceModelElement element = provider.getElement(BeansConfig.this, componentDefinition);
    if (element instanceof IBean) {
      beans.put(element.getElementName(), (IBean) element);
    }
    else if (element instanceof IBeansComponent) {
      components.add((IBeansComponent) element);
    }
  }

  /**
   * Returns the current {@link IResource} that is being processed.
   */
  private IResource getCurrentResource() {
    if (currentResource != null) {
      return currentResource;
    }
    return file;
  }

  /**
   * Implementation of {@link ReaderEventListener} which populates the current instance of {@link IBeansConfig} with
   * data from the XML bean definition reader events.
   */
  class BeansConfigReaderEventListener implements ReaderEventListener {

    private IBeansConfig config;

    private Resource resource;

    private Map<String, IModelElementProvider> elementProviders;

    private Map<Resource, Set<ComponentDefinition>> componentDefinitionsCache;

    private Map<Resource, Set<ImportDefinition>> importDefinitionsCache;

    private Map<Resource, Set<AliasDefinition>> aliasDefinitionsCache;

    private Map<Resource, DocumentDefaultsDefinition> defaultDefinitionsCache;

    private SourceExtractor sourceExtractor;

    private DocumentAccessor documentAccessor;

    public BeansConfigReaderEventListener(IBeansConfig config, Resource resource, SourceExtractor sourceExtractor,
        DocumentAccessor documentAccessor) {
      this.config = config;
      this.resource = resource;
      this.elementProviders = NamespaceUtils.getElementProviders();
      this.componentDefinitionsCache = new HashMap<Resource, Set<ComponentDefinition>>();
      this.importDefinitionsCache = new HashMap<Resource, Set<ImportDefinition>>();
      this.aliasDefinitionsCache = new HashMap<Resource, Set<AliasDefinition>>();
      this.defaultDefinitionsCache = new HashMap<Resource, DocumentDefaultsDefinition>();
      this.sourceExtractor = sourceExtractor;
      this.documentAccessor = documentAccessor;
    }

    /**
     * {@inheritDoc}
     */
    public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
      if (defaultsDefinition instanceof DocumentDefaultsDefinition) {
        Object source = defaultsDefinition.getSource();

        if (source instanceof IModelSourceLocation) {
          Resource resource = ((IModelSourceLocation) source).getResource();
          addDefaultToCache((DocumentDefaultsDefinition) defaultsDefinition, resource);
        }
      }
    }

    /**
     * {@inheritDoc}
     */
    public void importProcessed(ImportDefinition importDefinition) {
      Object source = importDefinition.getSource();

      if (source instanceof IModelSourceLocation) {
        Resource resource = ((IModelSourceLocation) source).getResource();
        addImportToCache(importDefinition, resource);
      }
    }

    /**
     * {@inheritDoc}
     */
    public void aliasRegistered(AliasDefinition aliasDefinition) {
      Object source = aliasDefinition.getSource();

      if (source instanceof IModelSourceLocation) {
        Resource resource = ((IModelSourceLocation) source).getResource();
        addAliasToCache(aliasDefinition, resource);
      }
    }

    /**
     * Converts the given {@link ComponentDefinition} into a corresponding {@link ISourceModelElement} via a
     * namespace-specific {@link IModelElementProvider}. These providers are registered via the extension point
     * <code>org.springframework.ide.eclipse.beans.core.namespaces</code>.
     */
    public void componentRegistered(ComponentDefinition componentDefinition) {
      Object source = componentDefinition.getSource();

      // make sure to attach a default source location
      if (source == null && currentEncodedResource != null && currentEncodedResource.getResource() != null) {
        source = sourceExtractor.extractSource(documentAccessor.getCurrentElement(),
            currentEncodedResource.getResource());
        for (BeanDefinition beanDefinition : componentDefinition.getBeanDefinitions()) {
          if (beanDefinition.getSource() == null && beanDefinition instanceof AbstractBeanDefinition) {
            ((AbstractBeanDefinition) beanDefinition).setSource(source);
            ((AbstractBeanDefinition) beanDefinition).setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
          }
        }
      }

      // make sure nested BeanDefinitions have all the source extraction applied
      addSourceToNestedBeanDefinitions(componentDefinition);

      if (source instanceof IModelSourceLocation) {
        Resource resource = ((IModelSourceLocation) source).getResource();
        addComponentToCache(componentDefinition, resource);
      }
    }

    private void addAliasToCache(AliasDefinition aliasDefinition, Resource resource) {
      if (aliasDefinitionsCache.containsKey(resource)) {
        aliasDefinitionsCache.get(resource).add(aliasDefinition);
      }
      else {
        Set<AliasDefinition> aliasDefinitions = new LinkedHashSet<AliasDefinition>();
        aliasDefinitions.add(aliasDefinition);
        aliasDefinitionsCache.put(resource, aliasDefinitions);
      }
    }

    private void addSourceToNestedBeanDefinitions(ComponentDefinition componentDefinition) {
      if (componentDefinition instanceof CompositeComponentDefinition) {
        CompositeComponentDefinition compositeComponentDefinition = (CompositeComponentDefinition) componentDefinition;
        for (ComponentDefinition nestedComponentDefinition : compositeComponentDefinition.getNestedComponents()) {
          for (BeanDefinition beanDefinition : nestedComponentDefinition.getBeanDefinitions()) {
            if (!(beanDefinition.getSource() instanceof IModelSourceLocation)
                && beanDefinition instanceof AbstractBeanDefinition) {
              ((AbstractBeanDefinition) beanDefinition).setSource(sourceExtractor.extractSource(
                  beanDefinition.getSource(), resource));
            }
          }
        }

      }
    }

    private void addComponentToCache(ComponentDefinition componentDefinition, Resource resource) {
      if (componentDefinitionsCache.containsKey(resource)) {
        componentDefinitionsCache.get(resource).add(componentDefinition);
      }
      else {
        Set<ComponentDefinition> componentDefinitions = new LinkedHashSet<ComponentDefinition>();
        componentDefinitions.add(componentDefinition);
        componentDefinitionsCache.put(resource, componentDefinitions);
      }
    }

    private void addDefaultToCache(DocumentDefaultsDefinition defaultsDefinition, Resource resource) {
      if (!defaultDefinitionsCache.containsKey(resource)) {
        defaultDefinitionsCache.put(resource, defaultsDefinition);
      }
    }

    private void addImportToCache(ImportDefinition importDefinition, Resource resource) {
      if (importDefinitionsCache.containsKey(resource)) {
        importDefinitionsCache.get(resource).add(importDefinition);
      }
      else {
        Set<ImportDefinition> importDefinitions = new LinkedHashSet<ImportDefinition>();
        importDefinitions.add(importDefinition);
        importDefinitionsCache.put(resource, importDefinitions);
      }
    }

    public void registerComponents() {

      // Start with the root resource
      defaults = defaultDefinitionsCache.get(resource);
      Set<ComponentDefinition> componentDefinitions = componentDefinitionsCache.get(resource);
      if (componentDefinitions != null) {
        for (ComponentDefinition componentDefinition : componentDefinitions) {
          registerComponentDefinition(componentDefinition, elementProviders);
        }
      }

      Set<AliasDefinition> aliasDefinitions = aliasDefinitionsCache.get(resource);
      if (aliasDefinitions != null) {
        for (AliasDefinition aliasDefinition : aliasDefinitions) {
          aliases.put(aliasDefinition.getAlias(), new BeanAlias(config, aliasDefinition));
        }
      }

      Set<ImportDefinition> importDefinitions = importDefinitionsCache.get(resource);
      if (importDefinitions != null) {
        for (ImportDefinition importDefinition : importDefinitions) {
          processImportDefinition(importDefinition, config);
        }
      }
    }

    private void processImportDefinition(ImportDefinition importDefinition, IBeansConfig config) {
      BeansImport beansImport = new BeansImport(config, importDefinition);

      if (config instanceof BeansConfig) {
        imports.add(beansImport);
      }
      else if (config instanceof ImportedBeansConfig) {
        ((ImportedBeansConfig) config).addImport(beansImport);
      }

      if (((IBeansProject) getElementParent()).isImportsEnabled()) {
        Resource[] importedResources = importDefinition.getActualResources();

        for (Resource importedResource : importedResources) {
          ImportedBeansConfig importedBeansConfig = new ImportedBeansConfig(beansImport, importedResource,
              getType());
          importedBeansConfig.readConfig();
          beansImport.addImportedBeansConfig(importedBeansConfig);

          importedBeansConfig.setDefaults(defaultDefinitionsCache.get(importedResource));
          Set<ComponentDefinition> componentDefinitions = componentDefinitionsCache.get(importedResource);
          if (componentDefinitions != null) {
            for (ComponentDefinition componentDefinition : componentDefinitions) {
              String uri = NamespaceUtils.getNameSpaceURI(componentDefinition);
              IModelElementProvider provider = elementProviders.get(uri);
              if (provider == null) {
                provider = DEFAULT_ELEMENT_PROVIDER;
              }
              ISourceModelElement element = provider.getElement(importedBeansConfig, componentDefinition);
              if (element instanceof IBean) {
                importedBeansConfig.addBean((IBean) element);
              }
              else if (element instanceof IBeansComponent) {
                importedBeansConfig.addComponent((IBeansComponent) element);
              }
            }
          }

          Set<AliasDefinition> aliasDefinitions = aliasDefinitionsCache.get(importedResource);
          if (aliasDefinitions != null) {
            for (AliasDefinition aliasDefinition : aliasDefinitions) {
              importedBeansConfig.addAlias(new BeanAlias(importedBeansConfig, aliasDefinition));
            }
          }

          // Process nested imports
          Set<ImportDefinition> importDefinitions = importDefinitionsCache.get(importedResource);
          if (importDefinitions != null) {
            for (ImportDefinition nestedImportDefinition : importDefinitions) {
              processImportDefinition(nestedImportDefinition, importedBeansConfig);
            }
          }

          importedBeansConfig.readFinish();
        }
      }
    }
  }

  /**
   * {@link ResourcePatternResolver} that checks if <code>.class</code> resource are being requested.
   */
  class ClassResourceFilteringPatternResolver extends EclipsePathMatchingResourcePatternResolver implements
      ResourcePatternResolver {

    /**
     * Creates a new {@link ClassResourceFilteringPatternResolver}
     */
    public ClassResourceFilteringPatternResolver(IProject project, ClassLoader classLoader) {
      super(project, classLoader);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Resource getResource(String location) {
      // Pass package scanning through to the default pattern resolver. This is required for
      // component-scanning.
      if (location.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) {
        return super.getResource(location);
      }
      return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
      // Pass package scanning through to the default pattern resolver. This is required for
      // component-scanning.
      if (locationPattern.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) {
        return super.getResources(locationPattern);
      }
      return new Resource[0];
    }

  }

  /**
   * SAX {@link ErrorHandler} implementation that creates {@link ValidationProblem}s for all reported problems
   */
  class BeansConfigErrorHandler implements ErrorHandler {

    /**
     * {@inheritDoc}
     */
    public void warning(SAXParseException e) throws SAXException {
      problems.add(new ValidationProblem(IMarker.SEVERITY_WARNING, e.getMessage(), getCurrentResource(), e
          .getLineNumber()));
    }

    /**
     * {@inheritDoc}
     */
    public void error(SAXParseException e) throws SAXException {
      problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, e.getMessage(), getCurrentResource(), e
          .getLineNumber()));
    }

    /**
     * {@inheritDoc}
     */
    public void fatalError(SAXParseException e) throws SAXException {
      problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, e.getMessage(), getCurrentResource(), e
          .getLineNumber()));
    }
  }

  /**
   * Implementation of the Spring tooling API {@link ProblemReporter} interface. This implementation creates
   * {@link ValidationProblem}s for all reported problems.
   */
  class BeansConfigProblemReporter implements ProblemReporter {

    /**
     * {@inheritDoc}
     */
    public void fatal(Problem problem) {
      problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, getMessage(problem), getCurrentResource(),
          getLine(problem)));
      throw new BeanDefinitionParsingException(problem);
    }

    /**
     * {@inheritDoc}
     */
    public void error(Problem problem) {
      problems.add(new ValidationProblem(IMarker.SEVERITY_ERROR, getMessage(problem), getCurrentResource(),
          getLine(problem)));
    }

    /**
     * {@inheritDoc}
     */
    public void warning(Problem problem) {
      problems.add(new ValidationProblem(IMarker.SEVERITY_WARNING, getMessage(problem), getCurrentResource(),
          getLine(problem)));
    }

    /**
     * Returns the concatenated message form the {@link Problem} and the {@link Problem#getRootCause()}.
     */
    private String getMessage(Problem problem) {
      StringBuffer message = new StringBuffer(problem.getMessage());
      Throwable rootCause = problem.getRootCause();
      if (rootCause != null) {

        // Retrieve nested exception
        while (rootCause.getCause() != null) {
          rootCause = rootCause.getCause();
        }
        message.append(": ");
        message.append(rootCause.getMessage());
      }
      return message.toString();
    }

    /**
     * Returns the line of the problem by introspecting the {@link XmlSourceLocation}.
     */
    private int getLine(Problem problem) {
      Object source = problem.getLocation().getSource();
      if (source instanceof XmlSourceLocation) {
        return ((XmlSourceLocation) source).getStartLine();
      }
      else if (source instanceof Node) {
        return LineNumberPreservingDOMParser.getStartLineNumber((Node) source);
      }
      return -1;
    }
  }

  /**
   * Special {@link ReaderEventListener} implementation that passes
   * {@link ReaderEventListener#componentRegistered(ComponentDefinition)} calls to this configs
   * {@link BeansConfig#registerComponentDefinition(ComponentDefinition, Map)}.
   * @author Christian Dupuis
   * @since 2.2.5
   */
  class BeansConfigPostProcessorReaderEventListener extends EmptyReaderEventListener {

    // Keep the contributed model element providers
    final Map<String, IModelElementProvider> elementProviders = NamespaceUtils.getElementProviders();

    @Override
    public void componentRegistered(ComponentDefinition componentDefinition) {
      // make sure that all components that come through are safe for the model
      if (componentDefinition.getSource() == null) {
        if (componentDefinition instanceof BeanComponentDefinition) {
          ((AbstractBeanDefinition) ((BeanComponentDefinition) componentDefinition).getBeanDefinition())
              .setSource(new DefaultModelSourceLocation(1, 1, resource));
        }
      }
      registerComponentDefinition(componentDefinition, elementProviders);
    }
  }

  /**
   * Extension to {@link SimpleBeanDefinitionRegistry} that suppresses registrations of
   * {@link ScannedGenericBeanDefinition} instances as those contain references to the a classloader which we want to
   * discard.
   * @since 2.3.1
   */
  class ScannedGenericBeanDefinitionSuppressingBeanDefinitionRegistry extends SimpleBeanDefinitionRegistry {

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {
      if (beanDefinition instanceof ScannedGenericBeanDefinition) {
        super.registerBeanDefinition(beanName, new InternalScannedGenericBeanDefinition(
            (ScannedGenericBeanDefinition) beanDefinition));
      }
      else {
        super.registerBeanDefinition(beanName, beanDefinition);
      }
    }
  }

  /**
   * Alternative to {@link ScannedGenericBeanDefinition} that rejects the internal dependency to a ClassLoader hold by
   * the {@link AnnotationMetadata}.
   * @since 2.3.1
   */
  protected static class InternalScannedGenericBeanDefinition extends GenericBeanDefinition implements
      AnnotatedBeanDefinition {

    private static final long serialVersionUID = 467157320316462045L;

    public InternalScannedGenericBeanDefinition(AbstractBeanDefinition beanDefinition) {
      setBeanClassName(beanDefinition.getBeanClassName());
      setSource(beanDefinition.getSource());
      setResource(beanDefinition.getResource());
    }

    public AnnotationMetadata getMetadata() {
      return null;
    }
  }

  /**
   * Extension to the default {@link DefaultBeanDefinitionDocumentReader} to suppress import statements with
   * placeholders in resource attributes as this is not support in the IDE.
   * @since 2.3.1
   */
  static class ToolingFriendlyBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocumentReader {

    private Environment environment;
   
    private BeanDefinitionParserDelegate delegate;

    private BeansConfig beansConfig;

    public ToolingFriendlyBeanDefinitionDocumentReader(BeansConfig beansConfig) {
      this.beansConfig = beansConfig;
    }

    @Override
    public void setEnvironment(Environment environment) {
      super.setEnvironment(environment);
      this.environment = environment;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    protected void importBeanDefinitionResource(Element ele) {
      String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
      if (SpringCoreUtils.hasPlaceHolder(location)) {
        getReaderContext().warning("Resource location contains placeholder", ele);
      }
      else {
        super.importBeanDefinitionResource(ele);
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
      BeanDefinitionParserDelegate delegate = new ErrorSuppressingBeanDefinitionParserDelegate(readerContext, environment);
      delegate.initDefaults(root, parentDelegate);
      return delegate;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
      BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
      if (bdHolder != null) {
        // Obtain source before decorating; decoration might discard the original source location
        Object source = bdHolder.getSource();
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        // Set the original source in case it got discared by the decorator
        if (bdHolder.getSource() == null) {
          ((AbstractBeanDefinition) bdHolder.getBeanDefinition()).setSource(source);
        }

        try {
          // Register the final decorated instance
          BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
          getReaderContext().error(
              "Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
      }
    }
   
    @Override
    protected void doRegisterBeanDefinitions(Element root) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      String[] specifiedProfiles = null;
      if (StringUtils.hasText(profileSpec)) {
        specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      }
     
      // Spring 3.1 profile support; register composite component definition to carry nested beans
      ProfileAwareCompositeComponentDefinition cd = new ProfileAwareCompositeComponentDefinition(root.getNodeName(),
          getReaderContext().extractSource(root), specifiedProfiles);
      beansConfig.componentDefinitions.push(cd);
      try {
       
        // any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
 
        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
 
        this.delegate = parent;
      }
      finally {
        beansConfig.componentDefinitions.pop();
        getReaderContext().fireComponentRegistered(cd);
      }
    }
  }
 
  class ProfileAwareReaderContext extends XmlReaderContext {

    private ReaderEventListener eventListener;
   
    public ProfileAwareReaderContext(Resource resource, ProblemReporter problemReporter,
        ReaderEventListener eventListener, SourceExtractor sourceExtractor, XmlBeanDefinitionReader reader,
        NamespaceHandlerResolver namespaceHandlerResolver) {
      super(resource, problemReporter, eventListener, sourceExtractor, reader, namespaceHandlerResolver);
      this.eventListener = eventListener;
    }
   
    @Override
    public void fireComponentRegistered(ComponentDefinition componentDefinition) {
      if (!componentDefinitions.empty()) {
        componentDefinitions.peek().addNestedComponent(componentDefinition);
      }
      else {
        eventListener.componentRegistered(componentDefinition);
      }
    }
   
  }

  /**
   * Extension to {@link BeanDefinitionParserDelegate} that captures the class and linkage errors from loading
   * {@link NamespaceHandler}s instances.
   * @since 2.3.1
   */
  static class ErrorSuppressingBeanDefinitionParserDelegate extends BeanDefinitionParserDelegate {

    private final XmlReaderContext readerContext;

    public ErrorSuppressingBeanDefinitionParserDelegate(XmlReaderContext readerContext, Environment environment) {
      super(readerContext, environment);
      this.readerContext = readerContext;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
      String namespaceUri = getNamespaceURI(ele);
      try {
        readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
      }
      catch (FatalBeanException e) {
        // Beautify the exception message bit
        String msg = e.getMessage();
        int ix = msg.indexOf(';');
        if (ix > 0) {
          msg = msg.substring(0, ix);
        }
        readerContext.warning(msg + ". Check Error Log for more details.", ele);
        BeansCorePlugin.log(new Status(IStatus.WARNING, BeansCorePlugin.PLUGIN_ID,
            "Problem loading NamespaceHandler for '" + namespaceUri + "'.", e));
        return null;
      }
      return super.parseCustomElement(ele, containingBd);
    }
  }
}  
TOP

Related Classes of org.springframework.ide.eclipse.beans.core.internal.model.BeansConfig

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.