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.kernel.process;
016    
017    import aQute.bnd.annotation.ProviderType;
018    
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.process.ProcessConfig.Builder;
023    import com.liferay.portal.kernel.util.CharPool;
024    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
025    import com.liferay.portal.kernel.util.ServerDetector;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.URLCodec;
030    
031    import java.io.File;
032    import java.io.FileFilter;
033    
034    import java.lang.reflect.Method;
035    
036    import java.net.MalformedURLException;
037    import java.net.URI;
038    import java.net.URL;
039    import java.net.URLClassLoader;
040    import java.net.URLConnection;
041    
042    import java.util.Arrays;
043    import java.util.LinkedHashSet;
044    import java.util.Set;
045    
046    import javax.servlet.ServletContext;
047    import javax.servlet.ServletException;
048    
049    /**
050     * @author Shuyang Zhou
051     */
052    @ProviderType
053    public class ClassPathUtil {
054    
055            public static Set<URL> getClassPathURLs(ClassLoader classLoader) {
056                    Set<URL> urls = new LinkedHashSet<>();
057    
058                    while (classLoader != null) {
059                            if (classLoader instanceof URLClassLoader) {
060                                    URLClassLoader urlClassLoader = (URLClassLoader)classLoader;
061    
062                                    urls.addAll(Arrays.asList(urlClassLoader.getURLs()));
063                            }
064    
065                            classLoader = classLoader.getParent();
066                    }
067    
068                    return urls;
069            }
070    
071            public static URL[] getClassPathURLs(String classPath)
072                    throws MalformedURLException {
073    
074                    String[] paths = StringUtil.split(classPath, File.pathSeparatorChar);
075    
076                    Set<URL> urls = new LinkedHashSet<>();
077    
078                    for (String path : paths) {
079                            File file = new File(path);
080    
081                            URI uri = file.toURI();
082    
083                            urls.add(uri.toURL());
084                    }
085    
086                    return urls.toArray(new URL[urls.size()]);
087            }
088    
089            public static String getGlobalClassPath() {
090                    return _globalClassPath;
091            }
092    
093            public static String getJVMClassPath(boolean includeBootClassPath) {
094                    String jvmClassPath = System.getProperty("java.class.path");
095    
096                    if (includeBootClassPath) {
097                            String bootClassPath = System.getProperty("sun.boot.class.path");
098    
099                            jvmClassPath = jvmClassPath.concat(File.pathSeparator).concat(
100                                    bootClassPath);
101                    }
102    
103                    return jvmClassPath;
104            }
105    
106            public static String getPortalClassPath() {
107                    return _portalClassPath;
108            }
109    
110            public static ProcessConfig getPortalProcessConfig() {
111                    return _portalProcessConfig;
112            }
113    
114            public static void initializeClassPaths(ServletContext servletContext) {
115                    ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
116    
117                    if (classLoader == null) {
118                            _log.error("Portal ClassLoader is null");
119    
120                            return;
121                    }
122    
123                    StringBundler sb = new StringBundler(8);
124    
125                    String appServerGlobalClassPath = _buildClassPath(
126                            classLoader, ServletException.class.getName());
127    
128                    sb.append(appServerGlobalClassPath);
129                    sb.append(File.pathSeparator);
130    
131                    String portalGlobalClassPath = _buildClassPath(
132                            classLoader, PortalException.class.getName());
133    
134                    sb.append(portalGlobalClassPath);
135    
136                    _globalClassPath = sb.toString();
137    
138                    sb.append(File.pathSeparator);
139                    sb.append(
140                            _buildClassPath(
141                                    classLoader, "com.liferay.portal.servlet.MainServlet"));
142                    sb.append(File.pathSeparator);
143                    sb.append(servletContext.getRealPath(""));
144                    sb.append("/WEB-INF/classes");
145    
146                    _portalClassPath = sb.toString();
147    
148                    Builder builder = new Builder();
149    
150                    builder.setArguments(Arrays.asList("-Djava.awt.headless=true"));
151                    builder.setBootstrapClassPath(_globalClassPath);
152                    builder.setReactClassLoader(PortalClassLoaderUtil.getClassLoader());
153                    builder.setRuntimeClassPath(_portalClassPath);
154    
155                    _portalProcessConfig = builder.build();
156            }
157    
158            private static String _buildClassPath(
159                    ClassLoader classloader, String className) {
160    
161                    String pathOfClass = StringUtil.replace(
162                            className, CharPool.PERIOD, CharPool.SLASH);
163    
164                    pathOfClass = pathOfClass.concat(".class");
165    
166                    URL url = classloader.getResource(pathOfClass);
167    
168                    if (_log.isDebugEnabled()) {
169                            _log.debug("Build class path from " + url);
170                    }
171    
172                    String protocol = url.getProtocol();
173    
174                    if (protocol.equals("bundle") || protocol.equals("bundleresource")) {
175                            try {
176                                    URLConnection urlConnection = url.openConnection();
177    
178                                    Class<?> clazz = urlConnection.getClass();
179    
180                                    Method getLocalURLMethod = clazz.getDeclaredMethod(
181                                            "getLocalURL");
182    
183                                    getLocalURLMethod.setAccessible(true);
184    
185                                    url = (URL)getLocalURLMethod.invoke(urlConnection);
186                            }
187                            catch (Exception e) {
188                                    _log.error("Unable to resolve local URL from bundle", e);
189    
190                                    return StringPool.BLANK;
191                            }
192                    }
193    
194                    String path = URLCodec.decodeURL(url.getPath());
195    
196                    if (_log.isDebugEnabled()) {
197                            _log.debug("Path " + path);
198                    }
199    
200                    path = StringUtil.replace(path, CharPool.BACK_SLASH, CharPool.SLASH);
201    
202                    if (_log.isDebugEnabled()) {
203                            _log.debug("Decoded path " + path);
204                    }
205    
206                    if (ServerDetector.isWebLogic() && protocol.equals("zip")) {
207                            path = "file:".concat(path);
208                    }
209    
210                    if ((ServerDetector.isJBoss() || ServerDetector.isWildfly()) &&
211                            (protocol.equals("vfs") || protocol.equals("vfsfile"))) {
212    
213                            int pos = path.indexOf(".jar/");
214    
215                            if (pos != -1) {
216                                    String jarFilePath = path.substring(0, pos + 4);
217    
218                                    File jarFile = new File(jarFilePath);
219    
220                                    if (jarFile.isFile()) {
221                                            path = jarFilePath + '!' + path.substring(pos + 4);
222                                    }
223                            }
224    
225                            path = "file:".concat(path);
226                    }
227    
228                    File dir = null;
229    
230                    int pos = -1;
231    
232                    if (!path.startsWith("file:") ||
233                            ((pos = path.indexOf(CharPool.EXCLAMATION)) == -1)) {
234    
235                            if (!path.endsWith(pathOfClass)) {
236                                    _log.error(
237                                            "Class " + className + " is not loaded from a JAR file");
238    
239                                    return StringPool.BLANK;
240                            }
241    
242                            String classesDirName = path.substring(
243                                    0, path.length() - pathOfClass.length());
244    
245                            if (!classesDirName.endsWith("/WEB-INF/classes/")) {
246                                    _log.error(
247                                            "Class " + className + " is not loaded from a standard " +
248                                                    "location (/WEB-INF/classes)");
249    
250                                    return StringPool.BLANK;
251                            }
252    
253                            String libDirName = classesDirName.substring(
254                                    0, classesDirName.length() - "classes/".length());
255    
256                            libDirName += "/lib";
257    
258                            dir = new File(libDirName);
259                    }
260                    else {
261                            pos = path.lastIndexOf(CharPool.SLASH, pos);
262    
263                            dir = new File(path.substring("file:".length(), pos));
264                    }
265    
266                    if (!dir.isDirectory()) {
267                            _log.error(dir.toString() + " is not a directory");
268    
269                            return StringPool.BLANK;
270                    }
271    
272                    File[] files = dir.listFiles(
273                            new FileFilter() {
274    
275                                    @Override
276                                    public boolean accept(File file) {
277                                            if (file.isDirectory()) {
278                                                    return false;
279                                            }
280    
281                                            String name = file.getName();
282    
283                                            return name.endsWith(".jar");
284                                    }
285    
286                            });
287    
288                    if (files == null) {
289                            return StringPool.BLANK;
290                    }
291    
292                    Arrays.sort(files);
293    
294                    StringBundler sb = new StringBundler(files.length * 2);
295    
296                    for (File file : files) {
297                            sb.append(file.getAbsolutePath());
298                            sb.append(File.pathSeparator);
299                    }
300    
301                    sb.setIndex(sb.index() - 1);
302    
303                    return sb.toString();
304            }
305    
306            private static final Log _log = LogFactoryUtil.getLog(ClassPathUtil.class);
307    
308            private static String _globalClassPath;
309            private static String _portalClassPath;
310            private static ProcessConfig _portalProcessConfig;
311    
312    }