001    /**
002     * Copyright (c) 2000-2013 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.util.resiliency.spi.provider;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
021    import com.liferay.portal.kernel.resiliency.spi.SPIUtil;
022    import com.liferay.portal.kernel.resiliency.spi.provider.SPIProvider;
023    import com.liferay.portal.kernel.util.CharPool;
024    import com.liferay.portal.kernel.util.ClassUtil;
025    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
026    import com.liferay.portal.kernel.util.PropsKeys;
027    import com.liferay.portal.kernel.util.PropsUtil;
028    import com.liferay.portal.kernel.util.ReflectionUtil;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    
032    import java.io.File;
033    
034    import java.lang.reflect.Method;
035    
036    import java.util.Arrays;
037    import java.util.LinkedHashSet;
038    import java.util.Set;
039    import java.util.concurrent.atomic.AtomicReference;
040    
041    import javax.servlet.ServletContext;
042    import javax.servlet.ServletContextEvent;
043    import javax.servlet.ServletContextListener;
044    
045    /**
046     * @author Shuyang Zhou
047     */
048    public class SPIClassPathContextListener implements ServletContextListener {
049    
050            public static volatile String SPI_CLASS_PATH = StringPool.BLANK;
051    
052            @Override
053            public void contextDestroyed(ServletContextEvent servletContextEvent) {
054                    SPIProvider spiProvider = spiProviderReference.getAndSet(null);
055    
056                    if (spiProvider != null) {
057                            MPIHelperUtil.unregisterSPIProvider(spiProvider);
058                    }
059            }
060    
061            @Override
062            public void contextInitialized(ServletContextEvent servletContextEvent) {
063                    ServletContext servletContext = servletContextEvent.getServletContext();
064    
065                    String contextPath = servletContext.getRealPath(StringPool.BLANK);
066                    String spiEmbeddedLibDirName = servletContext.getInitParameter(
067                            "spiEmbeddedLibDir");
068    
069                    Set<File> jarFiles = new LinkedHashSet<File>();
070    
071                    // Load embedded Tomcat
072    
073                    File spiEmbeddedLibDir = new File(contextPath, spiEmbeddedLibDirName);
074    
075                    addJarFiles(jarFiles, spiEmbeddedLibDir);
076    
077                    // Load portal-service.jar from MPI
078    
079                    addJarFiles(
080                            jarFiles, PortalClassLoaderUtil.getClassLoader(),
081                            PortalException.class.getName());
082    
083                    // Load JDBC driver jars from MPI
084    
085                    addJarFiles(
086                            jarFiles, PortalClassLoaderUtil.getClassLoader(),
087                            PropsUtil.get(PropsKeys.JDBC_DEFAULT_DRIVER_CLASS_NAME));
088    
089                    // Load ext jars
090    
091                    addJarFiles(jarFiles, new File(spiEmbeddedLibDir, "ext"));
092    
093                    StringBundler sb = new StringBundler(jarFiles.size() * 2 + 4);
094    
095                    for (File file : jarFiles) {
096                            sb.append(file.getAbsolutePath());
097                            sb.append(File.pathSeparator);
098                    }
099    
100                    sb.append(contextPath);
101                    sb.append("/WEB-INF/classes");
102    
103                    SPI_CLASS_PATH = sb.toString();
104    
105                    if (_log.isDebugEnabled()) {
106                            _log.debug("SPI class path " + SPI_CLASS_PATH);
107                    }
108    
109                    String spiProviderClassName = servletContext.getInitParameter(
110                            "spiProviderClassName");
111    
112                    Thread currentThread = Thread.currentThread();
113    
114                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
115    
116                    try {
117                            Class<SPIProvider> spiProviderClass = null;
118    
119                            if (SPIUtil.isSPI()) {
120                                    spiProviderClass = (Class<SPIProvider>)loadClassDirectly(
121                                            contextClassLoader, spiProviderClassName);
122                            }
123                            else {
124                                    spiProviderClass =
125                                            (Class<SPIProvider>)contextClassLoader.loadClass(
126                                                    spiProviderClassName);
127                            }
128    
129                            SPIProvider spiProvider = spiProviderClass.newInstance();
130    
131                            boolean result = spiProviderReference.compareAndSet(
132                                    null, spiProvider);
133    
134                            if (!result) {
135                                    _log.error(
136                                            "Duplicate SPI provider " + spiProvider +
137                                                    " is already registered in servlet context " +
138                                                            servletContext.getContextPath());
139                            }
140                            else {
141                                    MPIHelperUtil.registerSPIProvider(spiProvider);
142                            }
143                    }
144                    catch (Exception e) {
145                            _log.error(
146                                    "Unable to create SPI provider with name " +
147                                            spiProviderClassName,
148                                    e);
149                    }
150            }
151    
152            protected static Class<?> loadClassDirectly(
153                            ClassLoader classLoader, String className)
154                    throws Exception {
155    
156                    synchronized (classLoader) {
157                            Method findLoadedClassMethod = ReflectionUtil.getDeclaredMethod(
158                                    ClassLoader.class, "findLoadedClass", String.class);
159    
160                            Class<?> clazz = (Class<?>)findLoadedClassMethod.invoke(
161                                    classLoader, className);
162    
163                            if (clazz == null) {
164                                    Method findClassMethod = ReflectionUtil.getDeclaredMethod(
165                                            ClassLoader.class, "findClass", String.class);
166    
167                                    clazz = (Class<?>)findClassMethod.invoke(
168                                            classLoader, className);
169                            }
170    
171                            Method resolveClassMethod = ReflectionUtil.getDeclaredMethod(
172                                    ClassLoader.class, "resolveClass", Class.class);
173    
174                            resolveClassMethod.invoke(classLoader, clazz);
175    
176                            return clazz;
177                    }
178            }
179    
180            protected void addJarFiles(
181                    Set<File> jarFiles, ClassLoader classLoader, String className) {
182    
183                    String path = ClassUtil.getParentPath(classLoader, className);
184    
185                    int pos = path.lastIndexOf(".jar");
186    
187                    pos = path.lastIndexOf(CharPool.SLASH, pos);
188    
189                    path = path.substring(0, pos);
190    
191                    addJarFiles(jarFiles, new File(path));
192            }
193    
194            protected void addJarFiles(Set<File> jarFiles, File dir) {
195                    if (!dir.exists() || !dir.isDirectory()) {
196                            throw new RuntimeException(
197                                    "Unable to find directory " + dir.getAbsolutePath());
198                    }
199    
200                    File[] files = dir.listFiles();
201    
202                    Arrays.sort(files);
203    
204                    for (File file : files) {
205                            String fileName = file.getName();
206    
207                            if (fileName.endsWith(".jar")) {
208                                    jarFiles.add(file);
209                            }
210                    }
211            }
212    
213            protected static final AtomicReference<SPIProvider> spiProviderReference =
214                    new AtomicReference<SPIProvider>();
215    
216            private static Log _log = LogFactoryUtil.getLog(
217                    SPIClassPathContextListener.class);
218    
219    }