001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.discovery.jdk;
018    
019    import java.io.IOException;
020    import java.net.URL;
021    import java.util.Collections;
022    import java.util.Enumeration;
023    
024    import org.apache.commons.discovery.log.DiscoveryLogFactory;
025    import org.apache.commons.logging.Log;
026    
027    
028    /**
029     * @author Richard A. Sitze
030     */
031    public class JDK12Hooks extends JDKHooks {
032        
033        /**
034         * Logger
035         */
036        private static Log log = DiscoveryLogFactory.newLog(JDK12Hooks.class);
037        
038        
039        private static final ClassLoader systemClassLoader
040            = findSystemClassLoader();
041    
042        /**
043         * Must be implemented to use DiscoveryLogFactory
044         */
045        public static void setLog(Log _log) {
046            log = _log;
047        }
048        
049        /**
050         * Get the system property
051         *
052         * @param propName name of the property
053         * @return value of the property
054         */
055        public String getSystemProperty(final String propName) {
056            return (String)
057            java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
058                public Object run() {
059                    try {
060                        return System.getProperty(propName);
061                    } catch (SecurityException se){
062                        return null;
063                    }
064                }
065            });
066        }
067    
068        /**
069         * The thread context class loader is available for JDK 1.2
070         * or later, if certain security conditions are met.
071         * 
072         * @return The thread context class loader, if available.
073         *         Otherwise return null.
074         */
075        public ClassLoader getThreadContextClassLoader() {
076            ClassLoader classLoader;
077            
078            try {
079                classLoader = Thread.currentThread().getContextClassLoader();
080            } catch (SecurityException e) {
081                /**
082                 * SecurityException is thrown when
083                 * a) the context class loader isn't an ancestor of the
084                 *    calling class's class loader, or
085                 * b) if security permissions are restricted.
086                 * 
087                 * For (a), ignore and keep going.  We cannot help but also
088                 * ignore (b) with the logic below, but other calls elsewhere
089                 * (to obtain a class loader) will re-trigger this exception
090                 * where we can make a distinction.
091                 */
092                classLoader = null;  // ignore
093            }
094            
095            // Return the selected class loader
096            return classLoader;
097        }
098        
099        /**
100         * The system class loader is available for JDK 1.2
101         * or later, if certain security conditions are met.
102         * 
103         * @return The system class loader, if available.
104         *         Otherwise return null.
105         */
106        public ClassLoader getSystemClassLoader() {
107            return systemClassLoader;
108        }
109    
110        /**
111         * Implement ClassLoader.getResources for JDK 1.2
112         */
113        public Enumeration getResources(ClassLoader loader,
114                                        String resourceName)
115            throws IOException
116        {
117            /**
118             * The simple answer is/was:
119             *    return loader.getResources(resourceName);
120             * 
121             * However, some classloaders overload the behavior of getResource
122             * (loadClass, etc) such that the order of returned results changes
123             * from normally expected behavior.
124             * 
125             * Example: locate classes/resources from child ClassLoaders first,
126             *          parents last (in some J2EE environs).
127             * 
128             * The resource returned by getResource() should be the same as the
129             * first resource returned by getResources().  Unfortunately, this
130             * is not, and cannot be: getResources() is 'final' in the current
131             * JDK's (1.2, 1.3, 1.4).
132             * 
133             * To address this, the implementation of this method will
134             * return an Enumeration such that the first element is the
135             * results of getResource, and all trailing elements are
136             * from getResources.  On each iteration, we check so see
137             * if the resource (from getResources) matches the first resource,
138             * and eliminate the redundent element.
139             */
140            
141            final URL first = loader.getResource(resourceName);
142            
143            // XXX: Trying to avoid JBoss UnifiedClassLoader problem
144            
145            Enumeration resources;
146            
147            if(first == null) {
148                log.debug("Could not find resource: " + resourceName);
149                resources = Collections.enumeration(Collections.EMPTY_LIST);
150                
151            } else {
152            
153                try {
154                    
155                    resources = loader.getResources(resourceName);
156                    
157                } catch (RuntimeException ex) {
158                    log.error("Exception occured during attept to get " + resourceName 
159                            + " from " + first, ex);
160                    resources = Collections.enumeration(Collections.EMPTY_LIST);
161                }
162                
163                resources = getResourcesFromUrl(first, resources);
164            }
165            
166            return resources;
167        }
168        
169        private static Enumeration getResourcesFromUrl(final URL first, final Enumeration rest) {
170            return new Enumeration() {
171                private boolean firstDone = (first == null);
172                private URL next = getNext();
173                
174                public Object nextElement() {
175                    URL o = next;
176                    next = getNext();
177                    return o;
178                }
179    
180                public boolean hasMoreElements() {
181                    return next != null;
182                }
183                
184                private URL getNext() {
185                    URL n;
186                    
187                    if (!firstDone) {
188                        /**
189                         * First time through, use results of getReference()
190                         * if they were non-null.
191                         */
192                        firstDone = true;
193                        n = first;
194                    } else {
195                        /**
196                         * Subsequent times through,
197                         * use results of getReferences()
198                         * but take out anything that matches 'first'.
199                         * 
200                         * Iterate through list until we find one that
201                         * doesn't match 'first'.
202                         */
203                        n = null;
204                        while (rest.hasMoreElements()  &&  n == null) {
205                            n = (URL)rest.nextElement();
206                            if (first != null &&
207                                n != null &&
208                                n.equals(first))
209                            {
210                                n = null;
211                            }
212                        }
213                    }
214                    
215                    return n;
216                }
217            };
218        }
219        
220        static private ClassLoader findSystemClassLoader() {
221            ClassLoader classLoader;
222            
223            try {
224                classLoader = ClassLoader.getSystemClassLoader();
225            } catch (SecurityException e) {
226                /**
227                 * Ignore and keep going.
228                 */
229                classLoader = null;
230            }
231    
232            if (classLoader == null) {
233                SecurityManager security = System.getSecurityManager();
234                if (security != null) {
235                    try {
236                        security.checkCreateClassLoader();
237                        classLoader = new PsuedoSystemClassLoader();
238                    } catch (SecurityException se){
239                    }
240                }
241            }
242            
243            // Return the selected class loader
244            return classLoader;
245        }
246    }