001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.kernel.repository;
018    
019    import java.util.Collection;
020    import java.util.Collections;
021    import java.util.LinkedHashSet;
022    import java.util.Map;
023    import java.util.SortedSet;
024    import java.util.TreeSet;
025    import java.util.concurrent.ConcurrentHashMap;
026    
027    import org.apache.geronimo.gbean.GBeanInfo;
028    import org.apache.geronimo.gbean.GBeanInfoBuilder;
029    import org.apache.geronimo.kernel.config.Configuration;
030    
031    /**
032     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
033     */
034    public class DefaultArtifactResolver implements ArtifactResolver {
035        private final ArtifactManager artifactManager;
036        private final Collection<? extends ListableRepository> repositories;
037        private final Map<Artifact, Artifact> explicitResolution = new ConcurrentHashMap<Artifact, Artifact>();
038    
039        public DefaultArtifactResolver(ArtifactManager artifactManager, ListableRepository repository) {
040            this.artifactManager = artifactManager;
041            this.repositories = Collections.singleton(repository);
042        }
043    
044        public DefaultArtifactResolver(ArtifactManager artifactManager, Collection<? extends ListableRepository> repositories, Map<Artifact, Artifact> explicitResolution) {
045            this.artifactManager = artifactManager;
046            this.repositories = repositories;
047            if (explicitResolution != null) {
048                this.explicitResolution.putAll(explicitResolution);
049            }
050        }
051    
052        protected Map<Artifact, Artifact> getExplicitResolution() {
053            return explicitResolution;
054        }
055    
056        public Artifact generateArtifact(Artifact source, String defaultType) {
057            if(source.isResolved()) {
058                Artifact deAliased = explicitResolution.get(source);
059                if (deAliased !=  null) {
060                    return deAliased;
061                }
062                return source;
063            }
064            String groupId = source.getGroupId() == null ? Artifact.DEFAULT_GROUP_ID : source.getGroupId();
065            String artifactId = source.getArtifactId();
066            String type = source.getType() == null ? defaultType : source.getType();
067            Version version = source.getVersion() == null ? new Version(Long.toString(System.currentTimeMillis())) : source.getVersion();
068    
069            return new Artifact(groupId, artifactId, version, type);
070        }
071    
072        public Artifact queryArtifact(Artifact artifact) throws MultipleMatchesException {
073            Artifact[] all = queryArtifacts(artifact);
074            if(all.length > 1) {
075                throw new MultipleMatchesException(artifact);
076            }
077            return all.length == 0 ? null : all[0];
078        }
079    
080        public Artifact[] queryArtifacts(Artifact artifact) {
081            //see if there is an explicit resolution for this artifact.
082            Artifact deAliased = explicitResolution.get(artifact);
083            if (deAliased != null) {
084                artifact = deAliased;
085            }
086            LinkedHashSet<Artifact> set = new LinkedHashSet<Artifact>();
087            for (ListableRepository repository : repositories) {
088                set.addAll(repository.list(artifact));
089            }
090            return set.toArray(new Artifact[set.size()]);
091        }
092    
093        public LinkedHashSet<Artifact> resolveInClassLoader(Collection<Artifact> artifacts) throws MissingDependencyException {
094            return resolveInClassLoader(artifacts, Collections.<Configuration>emptySet());
095        }
096    
097        public LinkedHashSet<Artifact> resolveInClassLoader(Collection<Artifact> artifacts, Collection<Configuration> parentConfigurations) throws MissingDependencyException {
098            LinkedHashSet<Artifact> resolvedArtifacts = new LinkedHashSet<Artifact>();
099            for (Artifact artifact : artifacts) {
100                if (!artifact.isResolved()) {
101                    artifact = resolveInClassLoader(artifact, parentConfigurations);
102                }
103                resolvedArtifacts.add(artifact);
104            }
105            return resolvedArtifacts;
106        }
107    
108        public Artifact resolveInClassLoader(Artifact source) throws MissingDependencyException {
109            return resolveInClassLoader(source, Collections.<Configuration>emptySet());
110        }
111    
112        public Artifact resolveInClassLoader(Artifact source, Collection<Configuration> parentConfigurations) throws MissingDependencyException {
113            Artifact working = resolveVersion(parentConfigurations, source);
114            if (working == null || !working.isResolved()) {
115                //todo can parentConfigurations be included?
116                throw new MissingDependencyException(source);
117            }
118    
119            return working;
120        }
121    
122        private Artifact resolveVersion(Collection<Configuration> parentConfigurations, Artifact working) {
123            //see if there is an explicit resolution for this artifact.
124            Artifact deAliased = explicitResolution.get(working);
125            if (deAliased != null) {
126                working = deAliased;
127            }
128            if (working.isResolved()) {
129                return working;
130            }
131            SortedSet existingArtifacts;
132            if (artifactManager != null) {
133                existingArtifacts = artifactManager.getLoadedArtifacts(working);
134            } else {
135                existingArtifacts = new TreeSet();
136            }
137    
138            // if we have exactly one artifact loaded use its' version
139            if (existingArtifacts.size() == 1) {
140                return (Artifact) existingArtifacts.first();
141            }
142    
143    
144            // if we have no existing loaded artifacts grab the highest version from the repository
145            if (existingArtifacts.size() == 0) {
146                SortedSet<Artifact> list = new TreeSet<Artifact>();
147                for (ListableRepository repository : repositories) {
148                    list.addAll(repository.list(working));
149                }
150    
151                if (list.isEmpty()) {
152                    if ("xbean-naming".equals(working.getArtifactId())) {
153                        return new Artifact("org.apache.xbean", "xbean-naming", "2.8", "jar");
154                    } else {
155                        return null;
156                    }
157                }
158                return list.last();
159            }
160    
161            // more than one version of the artifact was loaded...
162    
163            // if one of parents already loaded the artifact, use that version
164            Artifact artifact = searchParents(parentConfigurations, working);
165            if (artifact != null) {
166                return artifact;
167            }
168    
169            // it wasn't declared by the parent so just use the highest verstion already loaded
170            return (Artifact) existingArtifacts.last();
171        }
172    
173        private Artifact searchParents(Collection<Configuration> parentConfigurations, Artifact working) {
174            for (Configuration configuration : parentConfigurations) {
175    
176                // check if this parent matches the groupId, artifactId, and type
177                if (matches(configuration.getId(), working)) {
178                    return configuration.getId();
179                }
180    
181                Environment environment = configuration.getEnvironment();
182                if (environment.isInverseClassLoading()) {
183                    // Search dependencies of the configuration before searching the parents
184                    Artifact artifact = getArtifactVersion(configuration.getDependencies(), working);
185                    if (artifact != null) {
186                        return artifact;
187                    }
188    
189                    // wasn't declared in the dependencies, so search the parents of the configuration
190                    artifact = searchParents(configuration.getClassParents(), working);
191                    if (artifact != null) {
192                        return artifact;
193                    }
194    
195                } else {
196                    // Search the parents before the dependencies of the configuration
197                    Artifact artifact = searchParents(configuration.getClassParents(), working);
198                    if (artifact != null) {
199                        return artifact;
200                    }
201    
202                    // wasn't declared in a parent check the dependencies of the configuration
203                    artifact = getArtifactVersion(configuration.getDependencies(), working);
204                    if (artifact != null) {
205                        return artifact;
206                    }
207                }
208            }
209            return null;
210        }
211    
212        private Artifact getArtifactVersion(Collection<Artifact> artifacts, Artifact query) {
213            for (Artifact artifact : artifacts) {
214                if (matches(artifact, query)) {
215                    return artifact;
216                }
217            }
218            return null;
219        }
220    
221        private boolean matches(Artifact candidate, Artifact query) {
222            return query.matches(candidate);
223        }
224    
225        public static final GBeanInfo GBEAN_INFO;
226    
227        static {
228            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(DefaultArtifactResolver.class, "ArtifactResolver");
229            infoFactory.addAttribute("explicitResolution", Map.class, true, true);
230            infoFactory.addReference("ArtifactManager", ArtifactManager.class, "ArtifactManager");
231            infoFactory.addReference("Repositories", ListableRepository.class, "Repository");
232            infoFactory.addInterface(ArtifactResolver.class);
233    
234            infoFactory.setConstructor(new String[]{
235                    "ArtifactManager",
236                    "Repositories",
237                    "explicitResolution"
238            });
239    
240    
241            GBEAN_INFO = infoFactory.getBeanInfo();
242        }
243    
244        public static GBeanInfo getGBeanInfo() {
245            return GBEAN_INFO;
246        }
247    }