001    /* $Id: PluginManager.java 729122 2008-12-23 21:14:13Z rahul $
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     * 
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     * 
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */ 
018    
019    package org.apache.commons.digester.plugins;
020    
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Properties;
024    import java.util.Iterator;
025    
026    import org.apache.commons.digester.Digester;
027    
028    import org.apache.commons.logging.Log;
029    
030    /**
031     * Coordinates between PluginDeclarationRule and PluginCreateRule objects,
032     * providing a place to share data between instances of these rules.
033     * <p>
034     * One instance of this class exists per PluginRules instance.
035     *
036     * @since 1.6
037     */
038    
039    public class PluginManager {
040    
041        /** Map of classname->Declaration */
042        private HashMap<String, Declaration> declarationsByClass = new HashMap<String, Declaration>();
043    
044        /** Map of id->Declaration  */
045        private HashMap<String, Declaration> declarationsById = new HashMap<String, Declaration>();
046    
047        /** the parent manager to which this one may delegate lookups. */
048        private PluginManager parent;
049        
050        /** 
051         * The object containing data that should only exist once for each
052         * Digester instance.
053         */
054        private PluginContext pluginContext;
055        
056        //------------------- constructors ---------------------------------------
057        
058        /** Construct a "root" PluginManager, ie one with no parent. */
059        public PluginManager(PluginContext r) {
060            pluginContext = r;
061        }
062    
063        /** 
064         * Construct a "child" PluginManager. When declarations are added to
065         * a "child", they are stored within the child and do not modify the
066         * parent, so when the child goes out of scope, those declarations
067         * disappear. When asking a "child" to retrieve a declaration, it 
068         * delegates the search to its parent if it does not hold a matching
069         * entry itself.
070         * <p>
071         * @param parent must be non-null.
072         */
073        public PluginManager(PluginManager parent) {
074            this.parent = parent;
075            this.pluginContext = parent.pluginContext;
076        }
077        
078        //------------------- methods --------------------------------------------
079    
080        /**
081         * Add the declaration to the set of known declarations.
082         * <p>
083         * TODO: somehow get a reference to a Digester object
084         * so that we can really log here. Currently, all
085         * logging is disabled from this method.
086         *
087         *@param decl an object representing a plugin class.
088         */
089        public void addDeclaration(Declaration decl) {
090            Log log = LogUtils.getLogger(null);
091            boolean debug = log.isDebugEnabled();
092            
093            Class<?> pluginClass = decl.getPluginClass();
094            String id = decl.getId();
095            
096            declarationsByClass.put(pluginClass.getName(), decl);
097                
098            if (id != null) {
099                declarationsById.put(id, decl);
100                if (debug) {
101                    log.debug(
102                        "Indexing plugin-id [" + id + "]" +
103                        " -> class [" + pluginClass.getName() + "]");
104                }
105            }
106        }
107    
108        /**
109         * Return the declaration object with the specified class.
110         * If no such plugin is known, null is returned.
111         */
112        public Declaration getDeclarationByClass(String className) {
113            Declaration decl = 
114                (Declaration) declarationsByClass.get(className);
115                
116            if ((decl == null) && (parent != null)) {
117                decl = parent.getDeclarationByClass(className);
118            }
119    
120            return decl;
121        }
122    
123        /**
124         * Return the declaration object with the specified id.
125         * If no such plugin is known, null is returned.
126         *
127         *@param id Description of the Parameter
128         *@return The declaration value
129         */
130        public Declaration getDeclarationById(String id) {
131            Declaration decl = (Declaration) declarationsById.get(id);
132    
133            if ((decl == null) && (parent != null)) {
134                decl = parent.getDeclarationById(id);
135            }
136    
137            return decl;
138        }
139    
140        /**
141         * Given a plugin class and some associated properties, scan the
142         * list of known RuleFinder instances until one detects a source of
143         * custom rules for this plugin (aka a RuleLoader).
144         * <p>
145         * If no source of custom rules can be found, null is returned.
146         */
147        public RuleLoader findLoader(Digester digester, String id, 
148                            Class<?> pluginClass, Properties props) 
149                            throws PluginException {    
150    
151            // iterate over the list of RuleFinders, trying each one 
152            // until one of them locates a source of dynamic rules given
153            // this specific plugin class and the associated declaration 
154            // properties.
155            Log log = LogUtils.getLogger(digester);
156            boolean debug = log.isDebugEnabled();
157            log.debug("scanning ruleFinders to locate loader..");
158            
159            List<RuleFinder> ruleFinders = pluginContext.getRuleFinders();
160            RuleLoader ruleLoader = null;
161            try {
162                for(Iterator<RuleFinder> i = ruleFinders.iterator(); 
163                    i.hasNext() && ruleLoader == null; ) {
164                        
165                    RuleFinder finder = (RuleFinder) i.next();
166                    if (debug) {
167                        log.debug("checking finder of type " + finder.getClass().getName());
168                    }
169                    ruleLoader = finder.findLoader(digester, pluginClass, props);
170                }
171            }
172            catch(PluginException e) {
173                throw new PluginException(
174                    "Unable to locate plugin rules for plugin"
175                    + " with id [" + id + "]"
176                    + ", and class [" + pluginClass.getName() + "]"
177                    + ":" + e.getMessage(), e.getCause());
178            }
179            log.debug("scanned ruleFinders.");
180            
181            return ruleLoader;
182        }
183    }