001    /*
002     * Copyright (C) 2006-2007 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.codehaus.gmaven.runtime.loader;
018    
019    import org.codehaus.gmaven.feature.Provider;
020    import org.codehaus.gmaven.feature.ProviderLoader;
021    import org.codehaus.gmaven.feature.ProviderRegistry;
022    import org.codehaus.gmaven.feature.ProviderSelector;
023    import org.codehaus.plexus.PlexusConstants;
024    import org.codehaus.plexus.PlexusContainer;
025    import org.codehaus.plexus.context.Context;
026    import org.codehaus.plexus.context.ContextException;
027    import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
028    import org.slf4j.Logger;
029    import org.slf4j.LoggerFactory;
030    
031    import java.util.HashMap;
032    import java.util.Iterator;
033    import java.util.LinkedHashMap;
034    import java.util.Map;
035    import java.util.Set;
036    
037    /**
038     * Default {@link ProviderSelector}.
039     *
040     * @plexus.component role="org.codehaus.gmaven.feature.ProviderSelector"
041     *
042     * @version $Id: DefaultProviderSelector.java 52 2009-11-22 10:32:14Z user57 $
043     * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
044     */
045    public class DefaultProviderSelector
046        implements ProviderSelector, Contextualizable
047    {
048        private final Logger log = LoggerFactory.getLogger(getClass());
049    
050        private PlexusContainer container;
051    
052        public void contextualize(final Context context) throws ContextException {
053            assert context != null;
054    
055            container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
056        }
057    
058        protected PlexusContainer getContainer() {
059            if (container == null) {
060                throw new IllegalStateException("Container not bound");
061            }
062    
063            return container;
064        }
065    
066        public Provider select(final ProviderRegistry registry, final String selection) throws Exception {
067            assert registry != null;
068            assert selection != null;
069    
070            log.trace("Select: {}", selection);
071    
072            register(registry, selection);
073    
074            Provider provider = null;
075    
076            if (SELECT_ANY.equals(selection)) {
077                provider = selectAny(registry);
078            }
079            else {
080                String[] keys = selection.split(",");
081    
082                for (int i=0; i<keys.length; i++) {
083                    Provider tmp = registry.lookup(keys[i]);
084                    
085                    if (tmp != null) {
086                        provider = tmp;
087                        break;
088                    }
089                }
090            }
091    
092            if (log.isTraceEnabled()) {
093                if (provider == null) {
094                    log.trace("No matching providers found for selection: {}", selection);
095                }
096                else if (!provider.supported()) {
097                    log.trace("Found unsupported provider matching selection: {}, found: {}", selection, provider);
098                }
099            }
100            
101            return provider;
102        }
103    
104        private Provider selectAny(final ProviderRegistry registry) {
105            assert registry != null;
106    
107            Map supported = registry.providers(true);
108            Provider provider = null;
109    
110            if (supported != null && !supported.isEmpty()) {
111                provider = (Provider) supported.values().iterator().next();
112            }
113    
114            return provider;
115        }
116    
117        private void register(final ProviderRegistry registry, final String selection) throws Exception {
118            assert registry != null;
119            assert selection != null;
120    
121            // First run discovery
122            Map discovered = discover(registry, selection);
123    
124            // If we didn't find anything then puke
125            if (discovered == null || discovered.isEmpty()) {
126                log.debug("No providers discovered for selection: {}", selection);
127            }
128            else {
129                log.debug("Registering {} providers:", String.valueOf(discovered.size()));
130    
131                for (Iterator iter = discovered.keySet().iterator(); iter.hasNext();) {
132                    String key = (String) iter.next();
133                    Provider provider = (Provider) discovered.get(key);
134    
135                    log.debug("    {} -> {}", key, provider);
136    
137                    // Complain if we found a mismatch of keys
138                    if (!key.equals(provider.key())) {
139                        log.warn("Found mismatch of provider key; discovered key: {}, provider's key: {}", key, provider.key());
140                    }
141    
142                    Provider replaced = registry.register(provider);
143    
144                    // Complain if we replaced a provider
145                    if (replaced != null) {
146                        log.warn("Replaced provider; key: {}, current: {}, replaced: {}", new Object[] { key, provider, replaced });
147                    }
148                }
149            }
150        }
151    
152        private Map discover(final ProviderRegistry registry, final String selection) throws Exception {
153            assert registry != null;
154            assert selection != null;
155    
156            log.debug("Discovering providers for selection: {}", selection);
157    
158            Map discovered = null;
159            String[] keys = selection.split(",");
160    
161            // Attempt to discover providers for each key
162            for (int i=0; i<keys.length; i++) {
163                // Don't attempt to discover psuedo providers
164                if (keys[i].equals(SELECT_ANY)) {
165                    continue;
166                }
167    
168                try {
169                    // First see if the registry already has a provider for this key
170                    if (registry.lookup(keys[i]) != null) {
171                        log.debug("Provider already registered for: {}", keys[i]);
172    
173                        // Skip loading and re-use the registered provider
174                        continue;
175                    }
176    
177                    Map found = load(keys[i]);
178    
179                    if (found != null && !found.isEmpty()) {
180                        // Late init the discovered providers map
181                        if (discovered == null) {
182                            discovered = new HashMap();
183                        }
184    
185                        discovered.putAll(found);
186                    }
187                }
188                catch (Exception e) {
189                    log.debug("Failed to load providers for key: {}", keys[i], e);
190                }
191            }
192    
193            return discovered;
194        }
195    
196        protected Map load(final String key) throws Exception {
197            assert key != null;
198    
199            Map found = null;
200            Map loaders = findLoaders();
201    
202            if (loaders == null || loaders.isEmpty()) {
203                log.debug("No provider loaders were found");
204            }
205            else {
206                log.debug("Looking for provider {} in {}", key, loaders);
207    
208                for (Iterator iter = loaders.values().iterator(); iter.hasNext();) {
209                    ProviderLoader loader = (ProviderLoader) iter.next();
210    
211                    log.debug("Trying to load {} from {}", key, loader);
212    
213                    try {
214                        Map loaded = loader.load(key);
215    
216                        if (loaded != null && !loaded.isEmpty()) {
217                            found = loaded;
218                            break;
219                        }
220                    }
221                    catch (Exception e) {
222                        log.warn("Failed to load provider from: {}", loader, e);
223                    }
224                }
225            }
226    
227            return found;
228        }
229    
230        /**
231         * Find any provider loaders which are available in the container.
232         */
233        private Map findLoaders() {
234            Map loaders = getContainer().getComponentDescriptorMap(ProviderLoader.class.getName());
235            if (loaders == null) {
236                throw new Error("No provider loaders found");
237            }
238            
239            Set keys = loaders.keySet();
240            Map found = null;
241            ProviderLoader defaultLoader = null;
242    
243            for (Iterator iter = keys.iterator(); iter.hasNext();) {
244                String key = (String)iter.next();
245    
246                ProviderLoader loader;
247                
248                try {
249                    loader = (ProviderLoader) getContainer().lookup(ProviderLoader.class.getName(), key);
250                }
251                catch (Exception e) {
252                    log.warn("Failed to lookup provider loader for key: {}", key, e);
253                    continue;
254                }
255    
256                if (loader != null) {
257                    if (found == null) {
258                        // we need an ordered map
259                        found = new LinkedHashMap();
260                    }
261                    if (key.equals(SELECT_DEFAULT)) {
262                        defaultLoader = loader;
263                    }
264                    else {
265                        found.put(key, loader);
266                    }
267                }
268            }
269    
270            // the default should be added at the end (as fallback)
271            assert defaultLoader != null;
272            found.put(SELECT_DEFAULT, defaultLoader);
273    
274            return found;
275        }
276    }