001/* $Id: PluginManager.java 992060 2010-09-02 19:09:47Z simonetripodi $
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
019package org.apache.commons.digester.plugins;
020
021import java.util.HashMap;
022import java.util.List;
023import java.util.Properties;
024import java.util.Iterator;
025
026import org.apache.commons.digester.Digester;
027
028import 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
039public 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            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 = 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 = 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}