001    package 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    
022    import java.util.HashMap;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Properties;
026    
027    import org.apache.commons.digester3.Digester;
028    import 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     */
038    public 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    }