001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.module.framework;
016    
017    import com.liferay.portal.kernel.util.ReflectionUtil;
018    
019    import java.io.IOException;
020    
021    import java.lang.reflect.Field;
022    import java.lang.reflect.Method;
023    
024    import java.net.URL;
025    import java.net.URLClassLoader;
026    
027    import java.util.ArrayList;
028    import java.util.Enumeration;
029    import java.util.Iterator;
030    import java.util.List;
031    
032    /**
033     * @author Miguel Pastor
034     */
035    public class ModuleFrameworkClassLoader extends URLClassLoader {
036    
037            public ModuleFrameworkClassLoader(URL[] urls, ClassLoader parent) {
038                    super(urls, parent);
039    
040                    Object tomcat8StartedState = null;
041                    Field tomcat8StateField = null;
042    
043                    try {
044                            Class<?> clazz = parent.getClass();
045                            ClassLoader classLoader = clazz.getClassLoader();
046    
047                            Class<?> lifecycleStateClass = classLoader.loadClass(
048                                    "org.apache.catalina.LifecycleState");
049    
050                            Method valueOfMethod = lifecycleStateClass.getMethod(
051                                    "valueOf", String.class);
052    
053                            tomcat8StartedState = valueOfMethod.invoke(null, "STARTED");
054    
055                            Class<?> WebappClassLoaderBaseClass = classLoader.loadClass(
056                                    "org.apache.catalina.loader.WebappClassLoaderBase");
057    
058                            tomcat8StateField = ReflectionUtil.getDeclaredField(
059                                    WebappClassLoaderBaseClass, "state");
060                    }
061                    catch (Exception e) {
062                            tomcat8StartedState = null;
063                            tomcat8StateField = null;
064                    }
065    
066                    _tomcat8StartedState = tomcat8StartedState;
067                    _tomcat8StateField = tomcat8StateField;
068            }
069    
070            @Override
071            public URL getResource(String name) {
072                    URL url = findResource(name);
073    
074                    if (url == null) {
075                            url = super.getResource(name);
076                    }
077    
078                    return url;
079            }
080    
081            @Override
082            public Enumeration<URL> getResources(String name) throws IOException {
083                    final List<URL> urls = new ArrayList<>();
084    
085                    urls.addAll(_buildURLs(null));
086    
087                    Enumeration<URL> localURLs = findResources(name);
088    
089                    urls.addAll(_buildURLs(localURLs));
090    
091                    Enumeration<URL> parentURLs = null;
092    
093                    ClassLoader parentClassLoader = getParent();
094    
095                    if (parentClassLoader != null) {
096                            parentURLs = parentClassLoader.getResources(name);
097                    }
098    
099                    urls.addAll(_buildURLs(parentURLs));
100    
101                    return new Enumeration<URL>() {
102    
103                            final Iterator<URL> iterator = urls.iterator();
104    
105                            @Override
106                            public boolean hasMoreElements() {
107                                    return iterator.hasNext();
108                            }
109    
110                            @Override
111                            public URL nextElement() {
112                                    return iterator.next();
113                            }
114    
115                    };
116            }
117    
118            @Override
119            protected Class<?> loadClass(String name, boolean resolve)
120                    throws ClassNotFoundException {
121    
122                    Object lock = getClassLoadingLock(name);
123    
124                    synchronized (lock) {
125                            Class<?> clazz = findLoadedClass(name);
126    
127                            if (clazz == null) {
128                                    try {
129                                            clazz = findClass(name);
130                                    }
131                                    catch (ClassNotFoundException cnfe) {
132                                            if (_tomcat8StateField == null) {
133                                                    clazz = super.loadClass(name, resolve);
134                                            }
135                                            else {
136                                                    clazz = _loadForTomcat8(cnfe, name, resolve);
137                                            }
138                                    }
139                            }
140    
141                            if (resolve) {
142                                    resolveClass(clazz);
143                            }
144    
145                            return clazz;
146                    }
147            }
148    
149            private List<URL> _buildURLs(Enumeration<URL> url) {
150                    if (url == null) {
151                            return new ArrayList<>();
152                    }
153    
154                    List<URL> urls = new ArrayList<>();
155    
156                    while (url.hasMoreElements()) {
157                            urls.add(url.nextElement());
158                    }
159    
160                    return urls;
161            }
162    
163            private Class<?> _loadForTomcat8(
164                            ClassNotFoundException cnfe, String name, boolean resolve)
165                    throws ClassNotFoundException {
166    
167                    ClassLoader classLoader = getParent();
168    
169                    try {
170                            Object state = _tomcat8StateField.get(classLoader);
171    
172                            if (state == _tomcat8StartedState) {
173                                    return super.loadClass(name, resolve);
174                            }
175    
176                            _tomcat8StateField.set(classLoader, _tomcat8StartedState);
177    
178                            try {
179                                    return super.loadClass(name, resolve);
180                            }
181                            finally {
182                                    _tomcat8StateField.set(classLoader, state);
183                            }
184                    }
185                    catch (ReflectiveOperationException roe) {
186                            cnfe.addSuppressed(roe);
187    
188                            throw cnfe;
189                    }
190            }
191    
192            static {
193                    ClassLoader.registerAsParallelCapable();
194            }
195    
196            private final Object _tomcat8StartedState;
197            private final Field _tomcat8StateField;
198    
199    }