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