3656c859b825b55f0be31b7eff417a7c8b341bb9
[yangtools.git] / yang / yang-maven-plugin / src / main / java / org / opendaylight / yangtools / yang2sources / plugin / Util.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang2sources.plugin;
9
10 import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
11 import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.LOG_PREFIX;
12 import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING;
13 import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING_JAR;
14
15 import com.google.common.io.ByteSource;
16 import com.google.common.io.ByteStreams;
17 import java.io.File;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Date;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.zip.ZipEntry;
28 import java.util.zip.ZipFile;
29 import org.apache.maven.artifact.Artifact;
30 import org.apache.maven.artifact.repository.ArtifactRepository;
31 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
32 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
33 import org.apache.maven.model.Dependency;
34 import org.apache.maven.model.Plugin;
35 import org.apache.maven.plugin.MojoFailureException;
36 import org.apache.maven.project.MavenProject;
37 import org.apache.maven.repository.RepositorySystem;
38 import org.opendaylight.yangtools.yang.common.QNameModule;
39 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
42 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
43 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 final class Util {
48
49     /**
50      * It isn't desirable to create instances of this class.
51      */
52     private Util() {
53     }
54
55     private static final Logger LOG = LoggerFactory.getLogger(Util.class);
56
57     static List<File> getClassPath(final MavenProject project) {
58         final List<File> dependencies = new ArrayList<>();
59         for (Artifact element : project.getArtifacts()) {
60             File asFile = element.getFile();
61             if (isJar(asFile) || asFile.isDirectory()) {
62                 dependencies.add(asFile);
63             }
64         }
65         return dependencies;
66     }
67
68     /**
69      * Read current project dependencies and check if it don't grab incorrect
70      * artifacts versions which could be in conflict with plugin dependencies.
71      *
72      * @param project
73      *            current project
74      * @param repoSystem
75      *            repository system
76      * @param localRepo
77      *            local repository
78      * @param remoteRepos
79      *            remote repositories
80      */
81     static void checkClasspath(final MavenProject project, final RepositorySystem repoSystem,
82             final ArtifactRepository localRepo, final List<ArtifactRepository> remoteRepos) {
83         Plugin plugin = project.getPlugin(YangToSourcesMojo.PLUGIN_NAME);
84         if (plugin == null) {
85             LOG.warn("{} {} not found, dependencies version check skipped", LOG_PREFIX, YangToSourcesMojo.PLUGIN_NAME);
86         } else {
87             Map<Artifact, Collection<Artifact>> pluginDependencies = new HashMap<>();
88             getPluginTransitiveDependencies(plugin, pluginDependencies, repoSystem, localRepo, remoteRepos);
89
90             Set<Artifact> projectDependencies = project.getDependencyArtifacts();
91             for (Map.Entry<Artifact, Collection<Artifact>> entry : pluginDependencies.entrySet()) {
92                 checkArtifact(entry.getKey(), projectDependencies);
93                 for (Artifact dependency : entry.getValue()) {
94                     checkArtifact(dependency, projectDependencies);
95                 }
96             }
97         }
98     }
99
100     /**
101      * Read transitive dependencies of given plugin and store them in map.
102      *
103      * @param plugin
104      *            plugin to read
105      * @param map
106      *            map, where founded transitive dependencies will be stored
107      * @param repoSystem
108      *            repository system
109      * @param localRepository
110      *            local repository
111      * @param remoteRepos
112      *            list of remote repositories
113      */
114     private static void getPluginTransitiveDependencies(final Plugin plugin,
115             final Map<Artifact, Collection<Artifact>> map, final RepositorySystem repoSystem,
116             final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepos) {
117
118         List<Dependency> pluginDependencies = plugin.getDependencies();
119         for (Dependency dep : pluginDependencies) {
120             Artifact artifact = repoSystem.createDependencyArtifact(dep);
121
122             ArtifactResolutionRequest request = new ArtifactResolutionRequest();
123             request.setArtifact(artifact);
124             request.setResolveTransitively(true);
125             request.setLocalRepository(localRepository);
126             request.setRemoteRepositories(remoteRepos);
127
128             ArtifactResolutionResult result = repoSystem.resolve(request);
129             Set<Artifact> pluginDependencyDependencies = result.getArtifacts();
130             map.put(artifact, pluginDependencyDependencies);
131         }
132     }
133
134     /**
135      * Check artifact against collection of dependencies. If collection contains
136      * artifact with same groupId and artifactId, but different version, logs a
137      * warning.
138      *
139      * @param artifact
140      *            artifact to check
141      * @param dependencies
142      *            collection of dependencies
143      */
144     private static void checkArtifact(final Artifact artifact, final Collection<Artifact> dependencies) {
145         for (org.apache.maven.artifact.Artifact d : dependencies) {
146             if (artifact.getGroupId().equals(d.getGroupId()) && artifact.getArtifactId().equals(d.getArtifactId())) {
147                 if (!(artifact.getVersion().equals(d.getVersion()))) {
148                     LOG.warn("{} Dependency resolution conflict:", LOG_PREFIX);
149                     LOG.warn("{} '{}' dependency [{}] has different version than one declared in current project [{}]"
150                             + ". It is recommended to fix this problem because it may cause compilation errors.",
151                             LOG_PREFIX, YangToSourcesMojo.PLUGIN_NAME, artifact, d);
152                 }
153             }
154         }
155     }
156
157     private static boolean isJar(final File element) {
158         return element.isFile() && element.getName().endsWith(".jar");
159     }
160
161     @SuppressWarnings("checkstyle:illegalCatch")
162     static List<YangTextSchemaSource> findYangFilesInDependenciesAsStream(final MavenProject project)
163             throws MojoFailureException {
164         try {
165             final List<File> filesOnCp = Util.getClassPath(project);
166             LOG.info("{} Searching for yang files in following dependencies: {}", LOG_PREFIX, filesOnCp);
167
168             final List<YangTextSchemaSource> yangsFromDependencies = new ArrayList<>();
169             for (File file : filesOnCp) {
170                 final List<String> foundFilesForReporting = new ArrayList<>();
171                 // is it jar file or directory?
172                 if (file.isDirectory()) {
173                     //FIXME: code duplicate
174                     File yangDir = new File(file, META_INF_YANG_STRING);
175                     if (yangDir.exists() && yangDir.isDirectory()) {
176                         File[] yangFiles = yangDir.listFiles(
177                             (dir, name) -> name.endsWith(RFC6020_YANG_FILE_EXTENSION) && new File(dir, name).isFile());
178                         for (final File yangFile : yangFiles) {
179                             foundFilesForReporting.add(yangFile.getName());
180                             yangsFromDependencies.add(YangTextSchemaSource.forFile(yangFile));
181                         }
182                     }
183                 } else {
184                     try (ZipFile zip = new ZipFile(file)) {
185                         final Enumeration<? extends ZipEntry> entries = zip.entries();
186                         while (entries.hasMoreElements()) {
187                             final ZipEntry entry = entries.nextElement();
188                             final String entryName = entry.getName();
189
190                             if (entryName.startsWith(META_INF_YANG_STRING_JAR) && !entry.isDirectory()
191                                     && entryName.endsWith(RFC6020_YANG_FILE_EXTENSION)) {
192                                 foundFilesForReporting.add(entryName);
193
194                                 yangsFromDependencies.add(YangTextSchemaSource.delegateForByteSource(
195                                     entryName.substring(entryName.lastIndexOf('/') + 1),
196                                     ByteSource.wrap(ByteStreams.toByteArray(zip.getInputStream(entry)))));
197                             }
198                         }
199                     }
200                 }
201                 if (foundFilesForReporting.size() > 0) {
202                     LOG.info("{} Found {} yang files in {}: {}", LOG_PREFIX, foundFilesForReporting.size(), file,
203                         foundFilesForReporting);
204                 }
205
206             }
207
208             return yangsFromDependencies;
209         } catch (Exception e) {
210             throw new MojoFailureException(e.getMessage(), e);
211         }
212     }
213
214     /**
215      * Find all dependencies which contains yang sources.
216      * Returns collection of YANG files and Zip files which contains YANG files.
217      *
218      */
219     //  FIXME: Rename to what class is actually doing.
220     @SuppressWarnings("checkstyle:illegalCatch")
221     static Collection<File> findYangFilesInDependencies(final MavenProject project) throws MojoFailureException {
222         final List<File> yangsFilesFromDependencies = new ArrayList<>();
223
224         final List<File> filesOnCp;
225         try {
226             filesOnCp = Util.getClassPath(project);
227         } catch (Exception e) {
228             throw new MojoFailureException("Failed to scan for YANG files in dependencies", e);
229         }
230         LOG.info("{} Searching for yang files in following dependencies: {}", LOG_PREFIX, filesOnCp);
231
232         for (File file : filesOnCp) {
233             try {
234                 // is it jar file or directory?
235                 if (file.isDirectory()) {
236                     //FIXME: code duplicate
237                     File yangDir = new File(file, META_INF_YANG_STRING);
238                     if (yangDir.exists() && yangDir.isDirectory()) {
239                         File[] yangFiles = yangDir.listFiles(
240                             (dir, name) -> name.endsWith(RFC6020_YANG_FILE_EXTENSION) && new File(dir, name).isFile());
241
242                         yangsFilesFromDependencies.addAll(Arrays.asList(yangFiles));
243                     }
244                 } else {
245                     try (ZipFile zip = new ZipFile(file)) {
246
247                         final Enumeration<? extends ZipEntry> entries = zip.entries();
248                         while (entries.hasMoreElements()) {
249                             ZipEntry entry = entries.nextElement();
250                             String entryName = entry.getName();
251
252                             if (entryName.startsWith(META_INF_YANG_STRING_JAR)
253                                     && !entry.isDirectory() && entryName.endsWith(RFC6020_YANG_FILE_EXTENSION)) {
254                                 LOG.debug("{} Found a YANG file in {}: {}", LOG_PREFIX, file, entryName);
255                                 yangsFilesFromDependencies.add(file);
256                                 break;
257                             }
258                         }
259                     }
260                 }
261             } catch (Exception e) {
262                 throw new MojoFailureException("Failed to scan for YANG files in dependency: " + file.toString(), e);
263             }
264         }
265         return yangsFilesFromDependencies;
266     }
267
268     static SourceIdentifier moduleToIdentifier(final Module module) {
269         final QNameModule mod = module.getQNameModule();
270         final Date rev = mod.getRevision();
271         final com.google.common.base.Optional<String> optRev;
272         if (SimpleDateFormatUtil.DEFAULT_DATE_REV.equals(rev)) {
273             optRev = com.google.common.base.Optional.absent();
274         } else {
275             optRev = com.google.common.base.Optional.of(mod.getFormattedRevision());
276         }
277
278         return RevisionSourceIdentifier.create(module.getName(), optRev);
279     }
280 }