/* * Copyright 2010 Outerthought bvba * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lilyproject.tools.mavenplugin.lilyruntimedepresolver; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectBuilder; import org.lilyproject.util.Version; import org.springframework.util.AntPathMatcher; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class LilyRuntimeProjectClasspath { protected XPathFactory xpathFactory = XPathFactory.newInstance(); private String lilyVersion; private ArtifactFilter filter; private ArtifactFactory artifactFactory; protected ArtifactResolver resolver; protected ArtifactRepository localRepository; private Log log; public LilyRuntimeProjectClasspath(Log log, ArtifactFilter filter, ArtifactFactory artifactFactory, ArtifactResolver resolver, ArtifactRepository localRepository) { this.filter = filter; this.artifactFactory = artifactFactory; this.resolver = resolver; this.localRepository = localRepository; this.log = log; this.lilyVersion = Version.readVersion("org.lilyproject", "lily-runtime-plugin"); } public Set<Artifact> getAllArtifacts(Set<Artifact> moduleArtifacts, List remoteRepositories) throws MojoExecutionException { Set<Artifact> result = new HashSet<Artifact>(); result.addAll(moduleArtifacts); for (Artifact moduleArtifact : moduleArtifacts) { result.addAll(getClassPathArtifacts(moduleArtifact, remoteRepositories)); } return result; } public ModuleArtifacts getModuleArtifactsFromLilyRuntimeConfig(File confDirectory, List remoteRepos) throws MojoExecutionException { File configFile = new File(confDirectory, "runtime/wiring.xml"); try { FileInputStream fis = null; try { fis = new FileInputStream(configFile); ModuleArtifacts result = new ModuleArtifacts(); result.artifacts = getModuleArtifactsFromLilyRuntimeConfig(fis, configFile.getAbsolutePath(), remoteRepos); return result; } finally { if (fis != null) { fis.close(); } } } catch (Exception e) { throw new MojoExecutionException("Error reading lily runtime XML configuration from " + configFile, e); } } public ModuleArtifacts getModuleArtifactsFromLilyRuntimeConfig(Set<Artifact> dependencies, String[] wiringPathPatterns, MavenProjectBuilder mavenProjectBuilder, List remoteRepos) throws MojoExecutionException { ModuleArtifacts result = new ModuleArtifacts(); result.artifacts = new HashSet<Artifact>(); result.remoteRepositories = new ArrayList(); boolean foundAtLeastOneWiring = false; try { // Search in the jars of all the direct dependencies of the project for the wiring file // (not sure if this won't be too slow? it's just to avoid the user having to specify the artifact) log.info("Searching " + dependencies.size() + " dependencies for " + wiringPathPatterns.length + " path patterns."); for (Artifact artifact : dependencies) { if ("jar".equals(artifact.getType())) { resolver.resolve(artifact, remoteRepos, localRepository); AntPathMatcher matcher = new AntPathMatcher(); ZipFile zipFile; zipFile = new ZipFile(artifact.getFile()); try { Enumeration<? extends ZipEntry> entryEnum = zipFile.entries(); while (entryEnum.hasMoreElements()) { ZipEntry zipEntry = entryEnum.nextElement(); for (String pattern : wiringPathPatterns) { if (matcher.match(pattern, zipEntry.getName())) { foundAtLeastOneWiring = true; log.info("Reading " + zipEntry.getName() + " from " + artifact.getFile()); Set<Artifact> moduleArtifacts = getModuleArtifactsFromLilyRuntimeConfig( zipFile.getInputStream(zipEntry), zipEntry.getName(), remoteRepos); MavenProject wiringSourceProject = mavenProjectBuilder.buildFromRepository(artifact, remoteRepos, localRepository); List repositories = wiringSourceProject.getRemoteArtifactRepositories(); result.artifacts.addAll(moduleArtifacts); result.remoteRepositories.addAll(repositories); } } } } finally { zipFile.close(); } } } } catch (Exception e) { throw new MojoExecutionException("Error searching/reading wiring.xml file from dependency jars.", e); } if (!foundAtLeastOneWiring) { throw new MojoExecutionException("No wiring xml's were found."); } return result; } public Set<Artifact> getModuleArtifactsFromLilyRuntimeConfig(InputStream wiringStream, String path, List remoteRepos) throws MojoExecutionException { Document configDoc; try { configDoc = parse(wiringStream); } catch (Exception e) { throw new MojoExecutionException("Error reading lily runtime wiring from " + path, e); } return getArtifacts(configDoc, "/*/modules/artifact", path, remoteRepos); } public Set<Artifact> getClassPathArtifacts(Artifact moduleArtifact, List remoteRepositories) throws MojoExecutionException { return getClassPathArtifacts(moduleArtifact, "LILY-INF/classloader.xml", remoteRepositories); } public Set<Artifact> getClassPathArtifacts(Artifact moduleArtifact, String entryPath, List remoteRepos) throws MojoExecutionException { ZipFile zipFile = null; InputStream is = null; Document classLoaderDocument; try { zipFile = new ZipFile(moduleArtifact.getFile()); ZipEntry zipEntry = zipFile.getEntry(entryPath); if (zipEntry == null) { log.debug("No " + entryPath + " found in " + moduleArtifact); return Collections.emptySet(); } else { is = zipFile.getInputStream(zipEntry); classLoaderDocument = parse(is); } } catch (Exception e) { throw new MojoExecutionException("Error reading " + entryPath + " from " + moduleArtifact, e); } finally { if (is != null) { try { is.close(); } catch (Exception e) { /* ignore */ } } if (zipFile != null) { try { zipFile.close(); } catch (Exception e) { /* ignore */ } } } return getArtifacts(classLoaderDocument, "/classloader/classpath/artifact", "classloader.xml from module " + moduleArtifact, remoteRepos); } protected Set<Artifact> getArtifacts(Document configDoc, String artifactXPath, String sourceDescr, List remoteRepos) throws MojoExecutionException { Set<Artifact> artifacts = new HashSet<Artifact>(); NodeList nodeList; try { nodeList = (NodeList)xpathFactory.newXPath().evaluate(artifactXPath, configDoc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new MojoExecutionException("Error resolving XPath expression " + artifactXPath + " on " + sourceDescr); } for (int i = 0; i < nodeList.getLength(); i++) { Element el = (Element)nodeList.item(i); String groupId = el.getAttribute("groupId"); String artifactId = el.getAttribute("artifactId"); String version = el.getAttribute("version"); String classifier = el.getAttribute("classifier"); if (version.equals("") && groupId.startsWith("org.lilyproject")) { version = lilyVersion; } if (classifier.equals("")) { classifier = null; } Artifact artifact = artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, "jar", classifier); if (filter == null || filter.include(artifact)) { if (!artifacts.contains(artifact)) { if (resolver != null) { try { resolver.resolve(artifact, remoteRepos, localRepository); } catch (Exception e) { throw new MojoExecutionException("Error resolving artifact listed in " + sourceDescr + ": " + artifact, e); } } artifacts.add(artifact); } } } return artifacts; } protected Document parse(InputStream is) throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); return dbf.newDocumentBuilder().parse(is); } public static interface ArtifactFilter { boolean include(Artifact artifact); } }