Java tutorial
/** * @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; } }