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.security.AccessController;
022    import java.security.PrivilegedAction;
023    import java.util.Collections;
024    import java.util.Enumeration;
025    import java.util.List;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    
030    /**
031     * JDK 1.2 Style Hooks implementation.
032     */
033    public class JDK12Hooks extends JDKHooks {
034    
035        /**
036         * Logger
037         */
038        private static Log log = LogFactory.getLog(JDK12Hooks.class);
039    
040        private static final ClassLoader systemClassLoader = findSystemClassLoader();
041    
042        /**
043         * Sets the {@code Log} for this class.
044         *
045         * @param _log This class {@code Log}
046         * @deprecated This method is not thread-safe
047         */
048        @Deprecated
049        public static void setLog(Log _log) {
050            log = _log;
051        }
052    
053        /**
054         * {@inheritDoc}
055         */
056        @Override
057        public String getSystemProperty(final String propName) {
058            return AccessController.doPrivileged(new PrivilegedAction<String>() {
059                public String run() {
060                    try {
061                        return System.getProperty(propName);
062                    } catch (SecurityException se){
063                        return null;
064                    }
065                }
066            });
067        }
068    
069        /**
070         * {@inheritDoc}
071         */
072        @Override
073        public ClassLoader getThreadContextClassLoader() {
074            ClassLoader classLoader;
075    
076            try {
077                classLoader = Thread.currentThread().getContextClassLoader();
078            } catch (SecurityException e) {
079                /*
080                 * SecurityException is thrown when
081                 * a) the context class loader isn't an ancestor of the
082                 *    calling class's class loader, or
083                 * b) if security permissions are restricted.
084                 *
085                 * For (a), ignore and keep going.  We cannot help but also
086                 * ignore (b) with the logic below, but other calls elsewhere
087                 * (to obtain a class loader) will re-trigger this exception
088                 * where we can make a distinction.
089                 */
090                classLoader = null;  // ignore
091            }
092    
093            // Return the selected class loader
094            return classLoader;
095        }
096    
097        /**
098         * {@inheritDoc}
099         */
100        @Override
101        public ClassLoader getSystemClassLoader() {
102            return systemClassLoader;
103        }
104    
105        /**
106         * {@inheritDoc}
107         */
108        @Override
109        public Enumeration<URL> getResources(ClassLoader loader, String resourceName) throws IOException {
110            /*
111             * The simple answer is/was:
112             *    return loader.getResources(resourceName);
113             *
114             * However, some classloaders overload the behavior of getResource
115             * (loadClass, etc) such that the order of returned results changes
116             * from normally expected behavior.
117             *
118             * Example: locate classes/resources from child ClassLoaders first,
119             *          parents last (in some J2EE environs).
120             *
121             * The resource returned by getResource() should be the same as the
122             * first resource returned by getResources().  Unfortunately, this
123             * is not, and cannot be: getResources() is 'final' in the current
124             * JDK's (1.2, 1.3, 1.4).
125             *
126             * To address this, the implementation of this method will
127             * return an Enumeration such that the first element is the
128             * results of getResource, and all trailing elements are
129             * from getResources.  On each iteration, we check so see
130             * if the resource (from getResources) matches the first resource,
131             * and eliminate the redundent element.
132             */
133    
134            final URL first = loader.getResource(resourceName);
135    
136            // XXX: Trying to avoid JBoss UnifiedClassLoader problem
137    
138            Enumeration<URL> resources;
139    
140            if (first == null) {
141                if (log.isDebugEnabled()) {
142                    log.debug("Could not find resource: " + resourceName);
143                }
144                List<URL> emptyURL = Collections.emptyList();
145                resources = Collections.enumeration(emptyURL);
146    
147            } else {
148    
149                try {
150    
151                    resources = loader.getResources(resourceName);
152    
153                } catch (RuntimeException ex) {
154                    log.error("Exception occured during attept to get " + resourceName
155                            + " from " + first, ex);
156                    List<URL> emptyURL = Collections.emptyList();
157                    resources = Collections.enumeration(emptyURL);
158                }
159    
160                resources = getResourcesFromUrl(first, resources);
161            }
162    
163            return resources;
164        }
165    
166        /**
167         * Enumerates resources URL.
168         *
169         * @param first The first URL in the enumeration sequence
170         * @param rest The URL enumeration
171         * @return A new resources URL enumeration
172         */
173        private static Enumeration<URL> getResourcesFromUrl(final URL first, final Enumeration<URL> rest) {
174            return new Enumeration<URL>() {
175    
176                private boolean firstDone = (first == null);
177    
178                private URL next = getNext();
179    
180                public URL nextElement() {
181                    URL o = next;
182                    next = getNext();
183                    return o;
184                }
185    
186                public boolean hasMoreElements() {
187                    return next != null;
188                }
189    
190                private URL getNext() {
191                    URL n;
192    
193                    if (!firstDone) {
194                        /*
195                         * First time through, use results of getReference()
196                         * if they were non-null.
197                         */
198                        firstDone = true;
199                        n = first;
200                    } else {
201                        /*
202                         * Subsequent times through,
203                         * use results of getReferences()
204                         * but take out anything that matches 'first'.
205                         *
206                         * Iterate through list until we find one that
207                         * doesn't match 'first'.
208                         */
209                        n = null;
210                        while (rest.hasMoreElements()  &&  n == null) {
211                            n = rest.nextElement();
212                            if (first != null &&
213                                n != null &&
214                                n.equals(first))
215                            {
216                                n = null;
217                            }
218                        }
219                    }
220    
221                    return n;
222                }
223            };
224        }
225    
226        /**
227         * Find the System {@code ClassLoader}.
228         *
229         * @return The System {@code ClassLoader}
230         */
231        static private ClassLoader findSystemClassLoader() {
232    
233            ClassLoader classLoader;
234    
235            try {
236                classLoader = ClassLoader.getSystemClassLoader();
237            } catch (SecurityException e) {
238                /*
239                 * Ignore and keep going.
240                 */
241                classLoader = null;
242            }
243    
244            if (classLoader == null) {
245                SecurityManager security = System.getSecurityManager();
246                if (security != null) {
247                    try {
248                        security.checkCreateClassLoader();
249                        classLoader = new PsuedoSystemClassLoader();
250                    } catch (SecurityException se){
251                    }
252                }
253            }
254    
255            // Return the selected class loader
256            return classLoader;
257        }
258    
259    }