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 */
017package org.apache.commons.discovery.jdk;
018
019import java.io.IOException;
020import java.net.URL;
021import java.security.AccessController;
022import java.security.PrivilegedAction;
023import java.util.Collections;
024import java.util.Enumeration;
025import java.util.List;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030/**
031 * JDK 1.2 Style Hooks implementation.
032 */
033public 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}