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<URL>();
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<URL>();
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.setBootstrapClassPath(_globalClassPath);
151                    builder.setReactClassLoader(PortalClassLoaderUtil.getClassLoader());
152                    builder.setRuntimeClassPath(_portalClassPath);
153    
154                    _portalProcessConfig = builder.build();
155            }
156    
157            private static String _buildClassPath(
158                    ClassLoader classloader, String className) {
159    
160                    String pathOfClass = StringUtil.replace(
161                            className, CharPool.PERIOD, CharPool.SLASH);
162    
163                    pathOfClass = pathOfClass.concat(".class");
164    
165                    URL url = classloader.getResource(pathOfClass);
166    
167                    if (_log.isDebugEnabled()) {
168                            _log.debug("Build class path from " + url);
169                    }
170    
171                    String protocol = url.getProtocol();
172    
173                    if (protocol.equals("bundle") || protocol.equals("bundleresource")) {
174                            try {
175                                    URLConnection urlConnection = url.openConnection();
176    
177                                    Class<?> clazz = urlConnection.getClass();
178    
179                                    Method getLocalURLMethod = clazz.getDeclaredMethod(
180                                            "getLocalURL");
181    
182                                    getLocalURLMethod.setAccessible(true);
183    
184                                    url = (URL)getLocalURLMethod.invoke(urlConnection);
185                            }
186                            catch (Exception e) {
187                                    _log.error("Unable to resolve local URL from bundle", e);
188    
189                                    return StringPool.BLANK;
190                            }
191                    }
192    
193                    String path = URLCodec.decodeURL(url.getPath());
194    
195                    if (_log.isDebugEnabled()) {
196                            _log.debug("Path " + path);
197                    }
198    
199                    path = StringUtil.replace(path, CharPool.BACK_SLASH, CharPool.SLASH);
200    
201                    if (_log.isDebugEnabled()) {
202                            _log.debug("Decoded path " + path);
203                    }
204    
205                    if (ServerDetector.isWebLogic() && protocol.equals("zip")) {
206                            path = "file:".concat(path);
207                    }
208    
209                    if (ServerDetector.isJBoss() &&
210                            (protocol.equals("vfs") || protocol.equals("vfsfile"))) {
211    
212                            int pos = path.indexOf(".jar/");
213    
214                            if (pos != -1) {
215                                    String jarFilePath = path.substring(0, pos + 4);
216    
217                                    File jarFile = new File(jarFilePath);
218    
219                                    if (jarFile.isFile()) {
220                                            path = jarFilePath + '!' + path.substring(pos + 4);
221                                    }
222                            }
223    
224                            path = "file:".concat(path);
225                    }
226    
227                    File dir = null;
228    
229                    int pos = -1;
230    
231                    if (!path.startsWith("file:") ||
232                            ((pos = path.indexOf(CharPool.EXCLAMATION)) == -1)) {
233    
234                            if (!path.endsWith(pathOfClass)) {
235                                    _log.error(
236                                            "Class " + className + " is not loaded from a JAR file");
237    
238                                    return StringPool.BLANK;
239                            }
240    
241                            String classesDirName = path.substring(
242                                    0, path.length() - pathOfClass.length());
243    
244                            if (!classesDirName.endsWith("/WEB-INF/classes/")) {
245                                    _log.error(
246                                            "Class " + className + " is not loaded from a standard " +
247                                                    "location (/WEB-INF/classes)");
248    
249                                    return StringPool.BLANK;
250                            }
251    
252                            String libDirName = classesDirName.substring(
253                                    0, classesDirName.length() - "classes/".length());
254    
255                            libDirName += "/lib";
256    
257                            dir = new File(libDirName);
258                    }
259                    else {
260                            pos = path.lastIndexOf(CharPool.SLASH, pos);
261    
262                            dir = new File(path.substring("file:".length(), pos));
263                    }
264    
265                    if (!dir.isDirectory()) {
266                            _log.error(dir.toString() + " is not a directory");
267    
268                            return StringPool.BLANK;
269                    }
270    
271                    File[] files = dir.listFiles(
272                            new FileFilter() {
273    
274                                    @Override
275                                    public boolean accept(File file) {
276                                            if (file.isDirectory()) {
277                                                    return false;
278                                            }
279    
280                                            String name = file.getName();
281    
282                                            return name.endsWith(".jar");
283                                    }
284    
285                            });
286    
287                    if (files == null) {
288                            return StringPool.BLANK;
289                    }
290    
291                    Arrays.sort(files);
292    
293                    StringBundler sb = new StringBundler(files.length * 2);
294    
295                    for (File file : files) {
296                            sb.append(file.getAbsolutePath());
297                            sb.append(File.pathSeparator);
298                    }
299    
300                    sb.setIndex(sb.index() - 1);
301    
302                    return sb.toString();
303            }
304    
305            private static Log _log = LogFactoryUtil.getLog(ClassPathUtil.class);
306    
307            private static String _globalClassPath;
308            private static String _portalClassPath;
309            private static ProcessConfig _portalProcessConfig;
310    
311    }