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