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.config;
018    
019    import java.util.List;
020    import java.util.Collection;
021    import java.util.ArrayList;
022    import java.util.Iterator;
023    import java.util.LinkedHashSet;
024    import java.util.Collections;
025    import java.util.Set;
026    import java.util.Stack;
027    import java.io.File;
028    import java.net.MalformedURLException;
029    
030    import org.apache.geronimo.kernel.repository.ArtifactResolver;
031    import org.apache.geronimo.kernel.repository.MissingDependencyException;
032    import org.apache.geronimo.kernel.repository.Dependency;
033    import org.apache.geronimo.kernel.repository.Artifact;
034    import org.apache.geronimo.kernel.repository.Repository;
035    import org.apache.geronimo.kernel.repository.ImportType;
036    
037    /**
038     * @version $Rev: 517813 $ $Date: 2007-03-13 14:37:38 -0400 (Tue, 13 Mar 2007) $
039     */
040    public class ConfigurationResolver {
041        private final Artifact configurationId;
042        private final ArtifactResolver artifactResolver;
043        private final Collection repositories;
044    
045        /**
046         * file or configstore used to resolve classpath parts
047         */
048        private final File baseDir;
049        private final ConfigurationStore configurationStore;
050    
051        /**
052         * For nested configurations, the module name will be non-null.
053         */
054        private final String moduleName;
055       
056    
057        public ConfigurationResolver(Artifact configurationId, File baseDir) {
058            if (configurationId == null)  throw new NullPointerException("configurationId is null");
059    
060            this.configurationId = configurationId;
061            this.baseDir = baseDir;
062            artifactResolver = null;
063            repositories = Collections.EMPTY_SET;
064            configurationStore = null;
065            moduleName = null;
066        }
067    
068        public ConfigurationResolver(ConfigurationData configurationData, Collection repositories, ArtifactResolver artifactResolver) {
069            if (configurationData == null)  throw new NullPointerException("configurationData is null");
070            if (repositories == null) repositories = Collections.EMPTY_SET;
071    
072            configurationId = configurationData.getId();
073            this.artifactResolver = artifactResolver;
074            this.repositories = repositories;
075            configurationStore = configurationData.getConfigurationStore();
076            if (null != configurationData.getInPlaceConfigurationDir()) {
077                baseDir = configurationData.getInPlaceConfigurationDir();
078            } else {
079                baseDir = configurationData.getConfigurationDir();
080            }
081            moduleName = null;
082        }
083    
084        private ConfigurationResolver(Artifact configurationId, ArtifactResolver artifactResolver, Collection repositories, File baseDir, ConfigurationStore configurationStore, String moduleName) {
085            this.configurationId = configurationId;
086            this.artifactResolver = artifactResolver;
087            this.repositories = repositories;
088            this.baseDir = baseDir;
089            this.configurationStore = configurationStore;
090            this.moduleName = moduleName;
091        }
092    
093        public ConfigurationResolver createChildResolver(String moduleName) {
094            if (moduleName == null) throw new NullPointerException("moduleName is null");
095            if (this.moduleName != null) {
096                moduleName = this.moduleName + '/' + moduleName;
097            }
098    
099            File childBaseDir = null;
100            if (baseDir != null) {
101                childBaseDir = new File(baseDir, moduleName);
102            }
103            return new ConfigurationResolver(configurationId, artifactResolver, repositories, childBaseDir, configurationStore, moduleName);
104        }
105    
106        public File resolve(Artifact artifact) throws MissingDependencyException {
107            for (Iterator j = repositories.iterator(); j.hasNext();) {
108                Repository repository = (Repository) j.next();
109                if (repository.contains(artifact)) {
110                    File file = repository.getLocation(artifact);
111                    return file;
112                }
113            }
114            throw new MissingDependencyException("Unable to resolve dependency " + artifact);
115        }
116    
117        public Set resolve(String pattern) throws MalformedURLException, NoSuchConfigException {
118            if (configurationStore != null) {
119                Set matches = configurationStore.resolve(configurationId, moduleName, pattern);
120                return matches;
121            } else if (baseDir != null) {
122                Set matches = IOUtil.search(baseDir, pattern);
123                return matches;
124            } else {
125                throw new IllegalStateException("No configurationStore or baseDir supplied so paths can not be resolved");
126            }
127        }
128    
129        public List resolveTransitiveDependencies(Collection parents, List dependencies) throws MissingDependencyException {
130            Stack<Dependency> parentStack = new Stack<Dependency>();
131            return internalResolveTransitiveDependencies(parents, dependencies, parentStack);
132        }
133    
134        private List internalResolveTransitiveDependencies(Collection parents, List dependencies, Stack parentStack) throws MissingDependencyException {
135            List resolvedDependencies = new ArrayList();
136            for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
137                Dependency dependency = resolveDependency(parents, (Dependency) iterator.next(), parentStack);
138    
139                if (!resolvedDependencies.contains(dependency)) {
140                    resolvedDependencies.add(dependency);
141    
142                    List childDependencies = getChildDependencies(dependency);
143                    if (!childDependencies.isEmpty()) {
144                        parentStack.push(dependency);
145                        childDependencies = internalResolveTransitiveDependencies(parents, childDependencies, parentStack);
146                        parentStack.pop();
147                        resolvedDependencies.addAll(childDependencies);
148                    }
149                }
150            }
151            return resolvedDependencies;
152        }
153    
154        private Dependency resolveDependency(Collection parents, Dependency dependency, Stack<Dependency> parentStack) throws MissingDependencyException {
155            Artifact artifact = dependency.getArtifact();
156    
157            // if it is already resolved we are done
158            if (artifact.isResolved()) {
159                return dependency;
160            }
161    
162            // we need an artifact resolver at this point
163            if (artifactResolver == null) {
164                throw new MissingDependencyException("Artifact is not resolved and there no artifact resolver available: " + artifact);
165            }
166            
167            // resolve the artifact
168            try {
169                artifact = artifactResolver.resolveInClassLoader(artifact, parents);
170            } catch (MissingDependencyException e) {
171                // I'm throwing away the original error as the new message is lost on the stack as
172                // most folks will drill down to the message on the bottom of the stack.
173                StringBuffer sb = new StringBuffer();
174                sb.append(e.getMessage().trim()+"\n"+"  Parent stack:\n");
175                boolean first = true;
176                for (Dependency d  : parentStack) {
177                    sb.append("         "+d.getArtifact().toString().trim()+(first?" (top)":"")+"\n");
178                    first = false;
179                }
180                throw new MissingDependencyException(sb.toString());
181            }
182    
183            // build a new dependency object to contain the resolved artifact
184            Dependency resolvedDependency = new Dependency(artifact, dependency.getImportType());
185            return resolvedDependency;
186        }
187    
188        private ArrayList getChildDependencies(Dependency dependency) {
189            ArrayList childDependencies = new ArrayList();
190            for (Iterator repositoryIterator = repositories.iterator(); repositoryIterator.hasNext();) {
191                Repository repository = (Repository) repositoryIterator.next();
192                if (repository.contains(dependency.getArtifact())) {
193                    // get the child artifacts
194                    LinkedHashSet childArtifacts = repository.getDependencies(dependency.getArtifact());
195                    for (Iterator artifactIterator = childArtifacts.iterator(); artifactIterator.hasNext();) {
196                        Artifact artifact = (Artifact) artifactIterator.next();
197                        // add each child as a classes-only dependency
198                        childDependencies.add(new Dependency(artifact,  ImportType.CLASSES));
199                    }
200                }
201            }
202            return childDependencies;
203        }
204    }