001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with 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,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.message.spi;
021    
022    
023    import java.lang.reflect.InvocationTargetException;
024    import java.lang.reflect.Method;
025    
026    import java.util.Properties;
027    import java.util.Hashtable;
028    
029    
030    /**
031     * Abstract Provider base class and factory for accessing berlib specific
032     * Provider implementations and their SPI implementation classes.
033     * 
034     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035     * @version $Revision: 725712 $
036     */
037    public abstract class Provider
038    {
039        /** Default BER Library provider class name */
040        public static final String DEFAULT_PROVIDER = "org.apache.directory.shared.ldap.codec.TwixProvider";
041    
042        /** BER Library provider class name property */
043        public static final String BERLIB_PROVIDER = "asn.1.berlib.provider";
044    
045        /** The default file searched for on CP to load default provider props. */
046        public static final String BERLIB_PROPFILE = "berlib.properties";
047    
048        /** A provider monitor key. */
049        public static final String PROVIDER_MONITOR_KEY = "asn.1.berlib.provider.monitor";
050    
051        /** Message to use when using defaults */
052        public static final String USING_DEFAULTS_MSG = "Could not find the ASN.1 berlib provider properties file: "
053            + "berlib.properties.\nFile is not present on the classpath " + "or in $JAVA_HOME/lib:\n\tjava.home = "
054            + System.getProperty( "java.home" ) + "\n\tjava.class.path = " + System.getProperty( "java.class.path" );
055    
056        /** Use the no-op monitor by default unless we find something else */
057        private static ProviderMonitor monitor;
058    
059        static
060        {
061            findMonitor( System.getProperties() );
062        }
063    
064    
065        /*
066         * Checks to see if the provider monitor has been set as a system property.
067         * If it has try to instantiate it and use it.
068         */
069        private static void findMonitor( Properties props )
070        {
071            if ( props.containsKey( PROVIDER_MONITOR_KEY ) )
072            {
073                String fqcn = System.getProperties().getProperty( PROVIDER_MONITOR_KEY );
074    
075                if ( fqcn != null )
076                {
077                    Class<?> mc;
078    
079                    try
080                    {
081                        mc = Class.forName( fqcn );
082                        monitor = ( ProviderMonitor ) mc.newInstance();
083                    }
084                    catch ( ClassNotFoundException e )
085                    {
086                        System.err.println( "provider monitor class " + fqcn + " not found" );
087                    }
088                    catch ( IllegalAccessException e )
089                    {
090                        System.err.println( "provider monitor class " + fqcn
091                            + " does not expose a public default constructor" );
092                    }
093                    catch ( InstantiationException e )
094                    {
095                        System.err.println( "provider monitor class " + fqcn + " failed during instantiation" );
096                    }
097                }
098            }
099    
100            if ( monitor == null )
101            {
102                monitor = ProviderMonitor.NOOP_MONITOR;
103            }
104        }
105    
106        // ------------------------------------------------------------------------
107        // Provider Properties
108        // ------------------------------------------------------------------------
109    
110        /** The descriptive string to identify this provider */
111        private final String name;
112    
113        /** The Provider's vendor name */
114        private final String vendor;
115    
116    
117        // ------------------------------------------------------------------------
118        // Constructors
119        // ------------------------------------------------------------------------
120    
121        /**
122         * Creates an instance of a Provider.
123         * 
124         * @param name
125         *            a descriptive name for a provider
126         * @param vendor
127         *            the berlib vendor used by the provider
128         */
129        protected Provider( String name, String vendor )
130        {
131            this.name = name;
132            this.vendor = vendor;
133        }
134    
135    
136        // ------------------------------------------------------------------------
137        // Property Accessor Methods
138        // ------------------------------------------------------------------------
139    
140        /**
141         * Gets the descriptive name for this Provider.
142         * 
143         * @return the Provider's name.
144         */
145        public final String getName()
146        {
147            return name;
148        }
149    
150    
151        /**
152         * Gets this Providers vendor name if it was provided.
153         * 
154         * @return the vendor name for this provider or the String 'UNKNOWN' if it
155         *         is not known.
156         */
157        public final String getVendor()
158        {
159            return vendor;
160        }
161    
162    
163        /**
164         * Gets the encoder associated with this provider.
165         * 
166         * @return the provider's encoder.
167         * @throws ProviderException
168         *             if the provider or its encoder cannot be found
169         */
170        public abstract ProviderEncoder getEncoder() throws ProviderException;
171    
172    
173        /**
174         * Gets the decoder associated with this provider.
175         * 
176         * @return the provider's decoder.
177         * @throws ProviderException if the provider or its decoder cannot be found
178         * @param binaryAttributeDetector detects whether or not attributes are binary
179         * @param maxPDUSize the maximum size a PDU can be
180         */
181        public abstract ProviderDecoder getDecoder( BinaryAttributeDetector binaryAttributeDetector,
182            int maxPDUSize )
183                throws ProviderException;
184    
185    
186        // ------------------------------------------------------------------------
187        // Factory/Environment Methods
188        // ------------------------------------------------------------------------
189    
190        
191        /**
192         * Gets an instance of the configured Provider. The configured provider is
193         * the classname specified by the <code>asn.1.berlib.provider</code>
194         * property. The property is searched for within berlib.properties files
195         * that are on the java.class.path. If at least one berlib.properties is not
196         * found the default provider is used. The resultant value (default or
197         * otherwise) for the property can be overridden by command line properties.
198         * 
199         * @return a singleton instance of the configured ASN.1 BER Library Provider
200         * @throws ProviderException
201         *             if the provider cannot be found
202         */
203        public static Provider getProvider() throws ProviderException
204        {
205            return getProvider( getEnvironment() );
206        }
207    
208    
209        /**
210         * Gets an instance of the Provider specified by the <code>
211         * asn.1.berlib.provider</code>
212         * property value. The property is searched for within properties object
213         * passed in as a parameter for this method only.
214         * 
215         * @param env
216         *            the environment used to locate the provider
217         * @return a singleton instance of the ASN.1 BER Library Provider
218         * @throws ProviderException
219         *             if the provider cannot be found
220         */
221        public static Provider getProvider( Hashtable<Object, Object> env ) throws ProviderException
222        {
223            Provider provider;
224            String className = ( String ) env.get( BERLIB_PROVIDER );
225    
226            // --------------------------------------------------------------------
227            // Check for a valid property value
228            // --------------------------------------------------------------------
229            if ( ( className == null ) || className.trim().equals( "" ) )
230            {
231                throw new ProviderException( null, "Could not instantiate provider - environment does not specify "
232                    + BERLIB_PROVIDER + " property!" );
233            }
234    
235            try
236            {
237                Class<?> clazz = Class.forName( className );
238                Method method = clazz.getMethod( "getProvider", (Class[])null );
239                provider = ( Provider ) method.invoke( null, (Object[])null );
240            }
241            catch ( ClassNotFoundException cnfe )
242            {
243                ProviderException pe = new ProviderException( null, "Count not find the Provider class " + className );
244                pe.addThrowable( cnfe );
245                throw pe;
246            }
247            catch ( NoSuchMethodException nsme )
248            {
249                ProviderException pe = new ProviderException( null, "Count not invoke the Provider's factory method: "
250                    + className + ".getProvider() - it may not exist!" );
251                pe.addThrowable( nsme );
252                throw pe;
253            }
254            catch ( IllegalAccessException iae )
255            {
256                ProviderException pe = new ProviderException( null, "Count not invoke the Provider's factory method: "
257                    + className + ".getProvider() - it does seem to be a public method!" );
258                pe.addThrowable( iae );
259                throw pe;
260            }
261            catch ( InvocationTargetException ite )
262            {
263                ProviderException pe = new ProviderException( null, "Call to Provider's factory method: " + className
264                    + ".getProvider() threw the following exception:\n" + ite.getTargetException() );
265                pe.addThrowable( ite.getTargetException() );
266                throw pe;
267            }
268    
269            return provider;
270        }
271    
272    
273        /**
274         * Loads the properties for the effective environment. First searches class
275         * path for the default berlib.properties file. If it cannot find the file
276         * on the classpath it loads the defaults in the default berlib.properties
277         * file found in $JAVA_HOME/lib/berlib.properties. If the default file is
278         * not found and no berlib.properties are found on the classpath then the
279         * default provider is used. Once the property is set overriding values are
280         * searched for in the System's properties specified at startup using the
281         * <code>-Dproperty=value</code><i>java</i> command-line arguments.
282         * 
283         * @return the environment properties TODO why are we not throwing
284         *         ProviderExceptions here?
285         */
286        public static Properties getEnvironment()
287        {
288            // Prop file not on classpath so we complain and use the default!
289            Properties env = new Properties();
290            env.setProperty( BERLIB_PROVIDER, DEFAULT_PROVIDER );
291            monitor.usingDefaults( USING_DEFAULTS_MSG, env );
292            return env;
293        }
294    }