1 /* Copyright 2004-2005 the original author or authors.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 package org.codehaus.groovy.grails.resolve;
16 
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentHashMap;
25 
26 import org.apache.ivy.core.module.descriptor.Configuration;
27 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
28 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
29 import org.apache.ivy.core.module.descriptor.ExcludeRule;
30 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
31 import org.apache.ivy.core.module.id.ArtifactId;
32 import org.apache.ivy.core.module.id.ModuleId;
33 import org.apache.ivy.core.module.id.ModuleRevisionId;
34 import org.apache.ivy.plugins.matcher.PatternMatcher;
35 
36 /**
37  * Base class for IvyDependencyManager with some logic implemented in Java.
38  *
39  * @author Graeme Rocher
40  * @since 1.3
41  */
42 @SuppressWarnings("serial")
43 public abstract class AbstractIvyDependencyManager {
44 
45     /*
46      * Out of the box Ivy configurations are:
47      *
48      * - build: Dependencies for the build system only
49      * - compile: Dependencies for the compile step
50      * - runtime: Dependencies needed at runtime but not for compilation (see above)
51      * - test: Dependencies needed for testing but not at runtime (see above)
52      * - provided: Dependencies needed at development time, but not during WAR deployment
53      */
54     public static Configuration BUILD_CONFIGURATION  = new Configuration("build",
55                                                                 Configuration.Visibility.PUBLIC,
56                                                                 "Build system dependencies",
57                                                                 new String[]{"default"},
58                                                                 true, null);
59 
60     public static Configuration COMPILE_CONFIGURATION = new Configuration("compile",
61                                                                 Configuration.Visibility.PUBLIC,
62                                                                 "Compile time dependencies",
63                                                                 new String[]{"default" },
64                                                                 true, null);
65 
66     public static Configuration RUNTIME_CONFIGURATION = new Configuration("runtime",
67                                                                 Configuration.Visibility.PUBLIC,
68                                                                 "Runtime time dependencies",
69                                                                 new String[]{"compile"},
70                                                                 true, null);
71 
72     public static Configuration TEST_CONFIGURATION = new Configuration("test",
73                                                                 Configuration.Visibility.PUBLIC,
74                                                                 "Testing dependencies",
75                                                                 new String[]{"runtime"},
76                                                                 true, null);
77 
78     public static Configuration PROVIDED_CONFIGURATION = new Configuration("provided",
79                                                                 Configuration.Visibility.PUBLIC,
80                                                                 "Dependencies provided by the container",
81                                                                 new String[]{"default"},
82                                                                 true, null);
83 
84     public static Configuration DOCS_CONFIGURATION = new Configuration("docs",
85             Configuration.Visibility.PUBLIC,
86             "Dependencies for the documenation engine",
87             new String[]{"build"},
88             true, null);
89 
90     public static List<Configuration> ALL_CONFIGURATIONS = Arrays.asList(
91             BUILD_CONFIGURATION,
92             COMPILE_CONFIGURATION,
93             RUNTIME_CONFIGURATION,
94             TEST_CONFIGURATION,
95             PROVIDED_CONFIGURATION,
96             DOCS_CONFIGURATION);
97 
98     Map<String, List<String>> configurationMappings = new HashMap<String, List<String>>() {{
99        put("runtime", Arrays.asList("default"));
100        put("build", Arrays.asList("default"));
101        put("compile", Arrays.asList("default"));
102        put("provided", Arrays.asList("default"));
103        put("docs", Arrays.asList("default"));
104        put("test", Arrays.asList("default"));
105     }};
106 
107     protected String[] configurationNames = configurationMappings.keySet().toArray(
108             new String[configurationMappings.size()]);
109     protected Set<ModuleId> modules = new HashSet<ModuleId>();
110     protected Set<ModuleRevisionId> dependencies = new HashSet<ModuleRevisionId>();
111     protected Set<DependencyDescriptor> dependencyDescriptors = new HashSet<DependencyDescriptor>();
112     protected Set<DependencyDescriptor> pluginDependencyDescriptors = new HashSet<DependencyDescriptor>();
113     protected Set<String> pluginDependencyNames = new HashSet<String>();
114     protected Set<String> metadataRegisteredPluginNames = new HashSet<String>();
115     protected Map<String, Collection<ModuleRevisionId>> orgToDepMap = new HashMap<String, Collection<ModuleRevisionId>>();
116 
117     protected Map<String, DependencyDescriptor> pluginNameToDescriptorMap =
118         new ConcurrentHashMap<String, DependencyDescriptor>();
119     protected String applicationName;
120     protected String applicationVersion;
121 
122     /**
123      * Obtains a set of dependency descriptors defined in the project
124      */
getDependencyDescriptors()125     Set<DependencyDescriptor> getDependencyDescriptors() {
126         return dependencyDescriptors;
127     }
128 
getMetadataRegisteredPluginNames()129     public Set<String> getMetadataRegisteredPluginNames() {
130         return metadataRegisteredPluginNames;
131     }
132 
setMetadataRegisteredPluginNames(Set<String> metadataRegisteredPluginNames)133     public void setMetadataRegisteredPluginNames(Set<String> metadataRegisteredPluginNames) {
134         this.metadataRegisteredPluginNames = metadataRegisteredPluginNames;
135     }
136 
137     /**
138      * Obtains a set of plugin dependency descriptors defined in the project
139      */
getPluginDependencyDescriptors()140     Set<DependencyDescriptor> getPluginDependencyDescriptors() {
141         return pluginDependencyDescriptors;
142     }
143 
144     /**
145      * Obtains a particular DependencyDescriptor by the plugin name
146      * @param pluginName The plugin name
147      * @return A DependencyDescriptor or null
148      */
getPluginDependencyDescriptor(String pluginName)149     public DependencyDescriptor getPluginDependencyDescriptor(String pluginName) {
150         return pluginNameToDescriptorMap.get(pluginName);
151     }
152 
153     /**
154      * Obtains a set of plugins this application is dependent onb
155      * @return A set of plugins names
156      */
getPluginDependencyNames()157     public Set<String> getPluginDependencyNames() { return pluginDependencyNames; }
158 
159     /**
160      * Obtains a list of dependencies defined in the project
161      */
getDependencies()162     public Set<ModuleRevisionId> getDependencies() { return this.dependencies; }
163 
getApplicationName()164     public String getApplicationName() {
165         return applicationName;
166     }
167 
setApplicationName(String applicationName)168     public void setApplicationName(String applicationName) {
169         this.applicationName = applicationName;
170     }
171 
getApplicationVersion()172     public String getApplicationVersion() {
173         return applicationVersion;
174     }
175 
setApplicationVersion(String applicationVersion)176     public void setApplicationVersion(String applicationVersion) {
177         this.applicationVersion = applicationVersion;
178     }
179 
getConfigurationNames()180     public String[] getConfigurationNames() {
181         return configurationNames;
182     }
183 
getConfigurationMappings()184     public Map<String, List<String>> getConfigurationMappings() {
185         return configurationMappings;
186     }
187 
188     /**
189      * Returns whether a plugin is transitive
190      *
191      * @param pluginName The name of the plugin
192      * @return True if the plugin is transitive
193      */
isPluginTransitive(String pluginName)194     public boolean isPluginTransitive(String pluginName) {
195         DependencyDescriptor dd = pluginNameToDescriptorMap.get(pluginName);
196         return dd == null || dd.isTransitive();
197     }
198 
199     /**
200      * Adds a dependency to the project
201      *
202      * @param revisionId The ModuleRevisionId instance
203      */
addDependency(ModuleRevisionId revisionId)204     public void addDependency(ModuleRevisionId revisionId) {
205         modules.add(revisionId.getModuleId());
206         dependencies.add(revisionId);
207         final String org = revisionId.getOrganisation();
208         if (orgToDepMap.containsKey(org)) {
209             orgToDepMap.get(org).add(revisionId);
210         }
211         else {
212             Collection<ModuleRevisionId> deps = new HashSet<ModuleRevisionId>();
213             deps.add(revisionId);
214             orgToDepMap.put(org, deps);
215         }
216     }
217 
createExcludeArtifactId(String excludeName)218     protected ArtifactId createExcludeArtifactId(String excludeName) {
219         return createExcludeArtifactId(excludeName, PatternMatcher.ANY_EXPRESSION);
220     }
221 
createExcludeArtifactId(String excludeName, String group)222     protected ArtifactId createExcludeArtifactId(String excludeName, String group) {
223         ModuleId mid = ModuleId.newInstance(group, excludeName);
224         return new ArtifactId(
225                 mid, PatternMatcher.ANY_EXPRESSION,
226                 PatternMatcher.ANY_EXPRESSION,
227                 PatternMatcher.ANY_EXPRESSION);
228     }
229 
230     /**
231      * Adds a dependency descriptor to the project
232      * @param dd The DependencyDescriptor instance
233      */
addDependencyDescriptor(DependencyDescriptor dd)234     public void addDependencyDescriptor(DependencyDescriptor dd) {
235         if (dd != null) {
236             dependencyDescriptors.add(dd);
237             addDependency(dd.getDependencyRevisionId());
238         }
239     }
240 
createModuleDescriptor()241     public ModuleDescriptor createModuleDescriptor() {
242         // This is a blatant hack: we use an organisation that is highly
243         // unlikely to conflict with the project's dependencies. The
244         // truth is, the dependency manager doesn't really care what the
245         // organisation is. See:
246         //
247         //    http://jira.codehaus.org/browse/GRAILS-6270
248         //
249         DefaultModuleDescriptor moduleDescriptor =
250             DefaultModuleDescriptor.newDefaultInstance(ModuleRevisionId.newInstance("org.grails.internal", applicationName, applicationVersion));
251 
252         // TODO: make configurations extensible
253         moduleDescriptor.addConfiguration(BUILD_CONFIGURATION);
254         moduleDescriptor.addConfiguration(COMPILE_CONFIGURATION);
255         moduleDescriptor.addConfiguration(RUNTIME_CONFIGURATION);
256         moduleDescriptor.addConfiguration(TEST_CONFIGURATION);
257         moduleDescriptor.addConfiguration(PROVIDED_CONFIGURATION);
258         moduleDescriptor.addConfiguration(DOCS_CONFIGURATION);
259         return moduleDescriptor;
260     }
261 
isExcludedFromPlugin(String plugin, String dependencyName)262     public boolean isExcludedFromPlugin(String plugin, String dependencyName) {
263         DependencyDescriptor dd = pluginNameToDescriptorMap.get(plugin);
264         if (dd == null) {
265             return false;
266         }
267 
268         if (!dd.isTransitive()) {
269             return true;
270         }
271 
272 
273         ArtifactId aid = createExcludeArtifactId(dependencyName);
274         return isExcludedFromPlugin(dd, aid);
275     }
276 
isExcludedFromPlugin(DependencyDescriptor currentPlugin, ArtifactId dependency)277     public boolean isExcludedFromPlugin(DependencyDescriptor currentPlugin, ArtifactId dependency) {
278         return currentPlugin != null && currentPlugin.doesExclude(configurationNames, dependency);
279     }
280 
getPluginExcludes(String plugin)281     public Set<String> getPluginExcludes(String plugin) {
282         Set<String> excludes = new HashSet<String>();
283         DependencyDescriptor dd = pluginNameToDescriptorMap.get(plugin);
284         if (dd != null)  {
285             for (ExcludeRule er : dd.getAllExcludeRules()) {
286                 excludes.add(er.getId().getName());
287             }
288         }
289         return excludes;
290     }
291 }
292