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.io.File;
020    import java.net.MalformedURLException;
021    import java.net.URL;
022    import java.util.ArrayList;
023    import java.util.Collection;
024    import java.util.Collections;
025    import java.util.LinkedHashSet;
026    import java.util.List;
027    import java.util.Set;
028    import java.util.Stack;
029    
030    import org.apache.geronimo.kernel.repository.Artifact;
031    import org.apache.geronimo.kernel.repository.ArtifactResolver;
032    import org.apache.geronimo.kernel.repository.Dependency;
033    import org.apache.geronimo.kernel.repository.ImportType;
034    import org.apache.geronimo.kernel.repository.MissingDependencyException;
035    import org.apache.geronimo.kernel.repository.Repository;
036    
037    /**
038     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
039     */
040    public class ConfigurationResolver {
041        private final Artifact configurationId;
042        private final ArtifactResolver artifactResolver;
043        private final Collection<? extends Repository> 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.emptySet();
064            configurationStore = null;
065            moduleName = null;
066        }
067    
068        public ConfigurationResolver(ConfigurationData configurationData, Collection<? extends Repository> repositories, ArtifactResolver artifactResolver) {
069            if (configurationData == null)  throw new NullPointerException("configurationData is null");
070            if (repositories == null) repositories = Collections.emptySet();
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<? extends Repository> 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 (Repository repository : repositories) {
108                if (repository.contains(artifact)) {
109                    File file = repository.getLocation(artifact);
110                    return file;
111                }
112            }
113            throw new MissingDependencyException(artifact);
114        }
115    
116        public Set<URL> resolve(String pattern) throws MalformedURLException, NoSuchConfigException {
117            if (configurationStore != null) {
118                Set<URL> matches = configurationStore.resolve(configurationId, moduleName, pattern);
119                return matches;
120            } else if (baseDir != null) {
121                Set<URL> matches = IOUtil.search(baseDir, pattern);
122                return matches;
123            } else {
124                throw new IllegalStateException("No configurationStore or baseDir supplied so paths can not be resolved");
125            }
126        }
127    
128        public List<Dependency> resolveTransitiveDependencies(Collection<Configuration> parents, List<Dependency> dependencies) throws MissingDependencyException {
129            Stack<Artifact> parentStack = new Stack<Artifact>();
130            return internalResolveTransitiveDependencies(parents, dependencies, parentStack);
131        }
132    
133        private List<Dependency> internalResolveTransitiveDependencies(Collection<Configuration> parents, List<Dependency> dependencies, Stack<Artifact> parentStack) throws MissingDependencyException {
134            List<Dependency> resolvedDependencies = new ArrayList<Dependency>();
135            for (Dependency dependency1 : dependencies) {
136                Dependency dependency = resolveDependency(parents, dependency1, parentStack);
137    
138                if (!resolvedDependencies.contains(dependency)) {
139                    resolvedDependencies.add(dependency);
140    
141                    List<Dependency> childDependencies = getChildDependencies(dependency);
142                    if (!childDependencies.isEmpty()) {
143                        parentStack.push(dependency.getArtifact());
144                        childDependencies = internalResolveTransitiveDependencies(parents, childDependencies, parentStack);
145                        parentStack.pop();
146                        resolvedDependencies.addAll(childDependencies);
147                    }
148                }
149            }
150            return resolvedDependencies;
151        }
152    
153        private Dependency resolveDependency(Collection<Configuration> parents, Dependency dependency, Stack<Artifact> parentStack) throws MissingDependencyException {
154            Artifact artifact = dependency.getArtifact();
155    
156            // we might need an artifact resolver at this point
157            if (artifactResolver == null) {
158                // if it is already resolved we are done
159                if (artifact.isResolved()) {
160                    return dependency;
161                }
162                throw new MissingDependencyException("Artifact is not resolved and there no artifact resolver available: ", artifact, parentStack);
163            }
164            
165            // resolve the artifact
166            try {
167                artifact = artifactResolver.resolveInClassLoader(artifact, parents);
168            } catch (MissingDependencyException e) {
169                e.setQuery(artifact);
170                e.setStack(parentStack);
171                throw e;
172            }
173    
174            // build a new dependency object to contain the resolved artifact
175            Dependency resolvedDependency = new Dependency(artifact, dependency.getImportType());
176            return resolvedDependency;
177        }
178    
179        private ArrayList<Dependency> getChildDependencies(Dependency dependency) {
180            ArrayList<Dependency> childDependencies = new ArrayList<Dependency>();
181            for (Repository repository : repositories) {
182                if (repository.contains(dependency.getArtifact())) {
183                    // get the child artifacts
184                    LinkedHashSet<Artifact> childArtifacts = repository.getDependencies(dependency.getArtifact());
185                    for (Artifact artifact : childArtifacts) {
186                        // add each child as a classes-only dependency
187                        childDependencies.add(new Dependency(artifact, ImportType.CLASSES));
188                    }
189                }
190            }
191            return childDependencies;
192        }
193    }