net.happyonroad.component.container.support.DefaultComponentResolver.java Source code

Java tutorial

Introduction

Here is the source code for net.happyonroad.component.container.support.DefaultComponentResolver.java

Source

/**
 * @author XiongJie, Date: 13-9-13
 */
package net.happyonroad.component.container.support;

import com.thoughtworks.xstream.XStream;
import net.happyonroad.component.container.ComponentResolver;
import net.happyonroad.component.container.MutableComponentRepository;
import net.happyonroad.component.core.Component;
import net.happyonroad.component.core.exception.DependencyNotMeetException;
import net.happyonroad.component.core.exception.InvalidComponentException;
import net.happyonroad.component.core.exception.InvalidComponentNameException;
import net.happyonroad.component.core.support.*;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;

import java.io.*;
import java.util.*;

/** ?pom xml? */
public class DefaultComponentResolver implements ComponentResolver {
    protected XStream xmlResolver;
    protected MutableComponentRepository repository;
    private Logger logger = LoggerFactory.getLogger(DefaultComponentResolver.class.getName());
    //DMmerge/unmerge??debug
    private Stack<DependencyManagement> dependencyManagements = new Stack<DependencyManagement>();

    public DefaultComponentResolver(MutableComponentRepository repository) {
        if (repository == null) {
            throw new NullPointerException("The repository can't be null");
        }
        this.repository = repository;
        customizeXstream();
    }

    protected void customizeXstream() {
        logger.trace("Customizing xstream engine");
        xmlResolver = new XStream();
        xmlResolver.alias("project", DefaultComponent.class);
        xmlResolver.alias("parent", Component.class, DefaultComponent.class);
        xmlResolver.alias("dependencyManagement", DependencyManagement.class);
        //xmlResolver.aliasType("parent", DefaultComponent.class);
        xmlResolver.aliasField("packaging", DefaultComponent.class, "type");

        String ignores = "optional release modelVersion prerequisites issueManagement ciManagement "
                + "inceptionYear mailingLists developers contributors licenses scm build profiles "
                + "repositories pluginRepositories reports reporting "
                + "distributionManagement organization relativePath";
        for (String field : ignores.split("\\s")) {
            xmlResolver.omitField(DefaultComponent.class, field);
        }
        xmlResolver.alias("dependency", Dependency.class);
        xmlResolver.alias("exclusion", Exclusion.class);
        xmlResolver.alias("module", String.class);
        xmlResolver.aliasField("modules", DefaultComponent.class, "moduleNames");
        xmlResolver.registerLocalConverter(DefaultComponent.class, "properties", new MavenPropertiesConverter());
        logger.trace("Customized  xstream engine");
    }

    @Override
    public Component resolveComponent(Dependency dependency, InputStream pomDotXml)
            throws DependencyNotMeetException, InvalidComponentNameException {
        //??????
        DefaultComponent component = (DefaultComponent) xmlResolver.fromXML(pomDotXml);
        DefaultComponent parent = (DefaultComponent) component.getParent();
        //?
        if (parent != null) {
            if (parent.getType() == null)
                parent.setType("pom");
            parent.splitVersionAndClassifier();
            if (component.getGroupId() == null) {
                component.setGroupId(parent.getGroupId());
            }
            if (component.getVersion() == null) {
                component.setVersion(parent.getVersion());
                component.setClassifier(parent.getClassifier());
            } else {
                component.splitVersionAndClassifier();
            }
        }
        if (!component.isSnapshot())
            component.setRelease(true);//???release
        if (component.getType() == null)
            component.setType("jar");//?jar
        try {
            //????
            component.validate();
            //???? sub modulereference parent?
            repository.addComponent(component);
            //?Parent?
            parent = processParent(dependency, component, parent);
            //??parent????parent
            component.interpolate();

            //? dependency management scope = importdependency?merge)
            DependencyManagement dm = processDependencyManagement(component, parent);

            //???
            dependencyManagements.push(dm);
            try {
                processDependencies(dependency, component);
            } finally {
                dependencyManagements.pop();
            }
            //??????????
            // ??????
            //????
            //processModules(component);

        } catch (DependencyNotMeetException e) {
            repository.removeComponent(component);
            throw e;
        } catch (InvalidComponentException e) {
            //InvalidParent: parent?
            //DependencyNotMeet: ??
            //InvalidComponent: ?????
            // ??
            //
            repository.removeComponent(component);
            throw e;
        }

        logger.debug("Resolved  {} from input stream", component);
        return component;
    }

    protected DefaultComponent processParent(Dependency childDependency, DefaultComponent component,
            DefaultComponent parent) throws InvalidComponentNameException, DependencyNotMeetException {
        if (parent == null)
            return null;
        //?parentversion?????parent version??
        String parentVersion = parent.getVersion();
        parentVersion = component.interpolate(parentVersion);
        // Replace with repository defined parent
        Dependency dependency = new Dependency(parent.getGroupId(), parent.getArtifactId(), parentVersion);
        dependency.setClassifier(parent.getClassifier());
        dependency.setType(parent.getType());
        dependency.setExclusions(childDependency.getExclusions());

        Component existParent;
        try {
            //??????
            //????????
            existParent = repository.resolveComponent(dependency);
            component.setParent(existParent);
            mergeDependencies(existParent, component);
            logger.trace("Attaching {} as parent of {}", existParent, component);
            return (DefaultComponent) existParent;
        } catch (DependencyNotMeetException e) {
            //????parent??? on-demandparent
            if (dependency.accept(e.getDependency())) {
                //?parent???parent
                logger.debug("Demanding {} as parent, because of: {}", parent, e.getMessage());

                repository.addComponent(parent);
            } else {
                throw new DependencyNotMeetException(component, dependency, e);
            }
        }
        return parent;
    }

    DependencyManagement processDependencyManagement(DefaultComponent component, DefaultComponent parent)
            throws InvalidComponentNameException {
        DependencyManagement dm = component.getDependencyManagement();
        if (!dm.isEmpty()) {
            //TO avoid concurrent modifications
            Set<Dependency> dependencies = new HashSet<Dependency>(dm.getDependencies());
            for (Dependency d : dependencies) {
                d.interpolate(component);
                if ("import".equalsIgnoreCase(d.getScope())) {
                    try {
                        Component importing = repository.resolveComponent(d);
                        dm.merge(importing.getDependencyManagement());
                    } catch (DependencyNotMeetException e) {
                        logger.debug("Dependency with import scope is not supported fully {}", e.getMessage());
                    }
                }
            }
        }
        if (parent != null)
            dm.merge(parent.getDependencyManagement());
        return dm;
    }

    protected void mergeDependencies(Component parent, DefaultComponent component) {
        for (Dependency parentDefined : parent.getDependencies()) {
            //parent defined:
            /*    <dependency>
                <groupId>jmock</groupId>
                <artifactId>jmock</artifactId>
                <version>${version.jmock}</version>
                <scope>test</scope>
              </dependency>
            */
            //?
            /*
            <dependency>
              <groupId>jmock</groupId>
              <artifactId>jmock</artifactId>
            </dependency>
             */
            Dependency defined = component.getDependency(parentDefined);
            if (defined != null) {
                defined.merge(parentDefined);
            }
        }
        for (Dependency parentDefined : parent.getDependencyManagement().getDependencies()) {
            Dependency defined = component.getDependency(parentDefined);
            if (defined != null) {
                defined.merge(parentDefined);
            }
        }
    }

    protected void processDependencies(Dependency dependency, DefaultComponent component)
            throws InvalidComponentNameException, DependencyNotMeetException {
        if (component.getDependencies().isEmpty())
            return;
        //???
        List<Dependency> dependencies = component.getDependencies();
        List<Component> dependedComponents = new ArrayList<Component>(dependencies.size());
        for (Dependency depended : dependencies) {
            depended.reform();//move the artifactId prefix with dot into group Id
            if (dependency.exclude(depended)) {
                logger.trace("Skip excluded {}", depended);
                continue;
            }
            //depended?
            if (dependency.hasExclusions()) {
                depended.exclude(dependency.getExclusions());
            }
            //?
            qualify(depended);
            if (!depended.isTest()) {//???Test??
                try {
                    Component dependedComponent = repository.resolveComponent(depended);
                    dependedComponents.add(dependedComponent);
                } catch (DependencyNotMeetException e) {
                    if (e.getComponent() == null)
                        e.setComponent(component);
                    //??compile time??
                    if (!depended.isOptional() && depended.isCompile()) {
                        if (e.getComponent() == component)
                            throw e;
                        else
                            throw new DependencyNotMeetException(component, depended, e);
                    } else {
                        logger.trace("Can't resolve {} dependency {}", depended.getScope(), depended);
                    }
                }
            } else {
                logger.trace("Skip {} dependency {}", depended.getScope(), depended);
            }
        }
        component.setDependedComponents(dependedComponents);
    }

    private void qualify(Dependency depended) {
        for (DependencyManagement dm : dependencyManagements) {
            dm.qualify(depended);
        }
    }

    @Override
    public Component resolveComponent(final Dependency dependency, Resource resource)
            throws InvalidComponentNameException, DependencyNotMeetException {
        logger.debug("Resolving {} from {}", dependency, resource);
        String originName = resource.getFilename();
        if (originName == null)
            originName = resource.getDescription();
        String fileName = FilenameUtils.getName(originName);
        Dependency basic = Dependency.parse(fileName);
        if (!dependency.accept(basic)) {
            throw new InvalidComponentNameException(
                    "The component file name: " + fileName + " does not satisfy the dependency: " + dependency);
        }
        DefaultComponent comp;
        InputStream stream = null;
        if (fileName.endsWith(".pom")) {
            try {
                stream = resource.getInputStream();
                comp = (DefaultComponent) resolveComponent(dependency, stream);
                comp.setUnderlyingResource(resource);
                if (!comp.isAggregating()) {
                    File jarFile = digJarFilePath(fileName);
                    if (jarFile != null) {
                        ComponentJarResource jarResource = new ComponentJarResource(dependency, jarFile.getName());
                        comp.setResource(jarResource);
                    } else {
                        logger.warn("Can't find jar file for {}", comp);
                    }
                }
            } catch (IOException e) {
                throw new IllegalArgumentException("The pom file does not exist: " + fileName);
            } finally {
                try {
                    if (stream != null)
                        stream.close();
                } catch (IOException ex) {
                    /**/}
            }
        } else {
            if (dependency.getVersion() == null) {
                dependency.setVersion(basic.getVersion());
            }
            ComponentJarResource jarResource = new ComponentJarResource(dependency, fileName);
            try {
                stream = jarResource.getPomStream();
                comp = (DefaultComponent) resolveComponent(dependency, stream);
                comp.setUnderlyingResource(resource);
                comp.setResource(jarResource);
            } catch (IOException e) {
                throw new InvalidComponentException(dependency.getGroupId(), dependency.getArtifactId(),
                        dependency.getVersion(), "jar", "Can't resolve pom.xml: " + e.getMessage());
            } finally {
                try {
                    if (stream != null)
                        stream.close();
                } catch (IOException ex) {
                    /**/}
            }
        }
        if (comp.getType() == null)
            comp.setType(dependency.getType());

        if (!dependency.accept(comp))
            throw new InvalidComponentNameException(
                    "The component file name: " + fileName + " conflict with its inner pom: " + comp.toString());
        comp.setClassLoader(new ComponentClassLoader(comp));
        return comp;
    }

    protected File digJarFilePath(String fileName) {
        String[] folders = { "lib", "repository", "boot" };
        String home = repository.getHome();
        for (String folder : folders) {
            String path = String.format("%s/%s/%s", home, folder, fileName);
            path = path.replace(".pom", ".jar");
            File file = new File(path);
            if (file.exists())
                return file;
        }
        return null;
    }

}