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.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.process.ClassPathUtil;
020    import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
021    import com.liferay.portal.kernel.resiliency.spi.provider.SPIProvider;
022    import com.liferay.portal.kernel.util.ReflectionUtil;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.kernel.util.StringPool;
025    
026    import java.io.File;
027    
028    import java.lang.reflect.Method;
029    
030    import java.util.ArrayList;
031    import java.util.List;
032    import java.util.concurrent.atomic.AtomicReference;
033    
034    import javax.servlet.ServletContext;
035    import javax.servlet.ServletContextEvent;
036    import javax.servlet.ServletContextListener;
037    
038    /**
039     * @author Shuyang Zhou
040     */
041    public class SPIClassPathContextListener implements ServletContextListener {
042    
043            public static volatile String SPI_CLASS_PATH = StringPool.BLANK;
044    
045            @Override
046            public void contextDestroyed(ServletContextEvent servletContextEvent) {
047                    SPIProvider spiProvider = spiProviderReference.getAndSet(null);
048    
049                    if (spiProvider != null) {
050                            MPIHelperUtil.unregisterSPIProvider(spiProvider);
051                    }
052            }
053    
054            @Override
055            public void contextInitialized(ServletContextEvent servletContextEvent) {
056                    ServletContext servletContext = servletContextEvent.getServletContext();
057    
058                    String contextPath = servletContext.getRealPath(StringPool.BLANK);
059                    String spiEmbeddedLibDirName = servletContext.getInitParameter(
060                            "spiEmbeddedLibDir");
061    
062                    File spiEmbeddedLibDir = new File(contextPath, spiEmbeddedLibDirName);
063    
064                    if (!spiEmbeddedLibDir.exists() || !spiEmbeddedLibDir.isDirectory()) {
065                            _log.error(
066                                    "Unable to find SPI embedded lib directory " +
067                                            spiEmbeddedLibDir.getAbsolutePath());
068    
069                            return;
070                    }
071    
072                    List<File> jarFiles = new ArrayList<File>();
073    
074                    for (File file : spiEmbeddedLibDir.listFiles()) {
075                            String fileName = file.getName();
076    
077                            if (fileName.endsWith(".jar")) {
078                                    jarFiles.add(file);
079                            }
080                    }
081    
082                    StringBundler sb = new StringBundler(jarFiles.size() * 2 + 4);
083    
084                    for (File file : jarFiles) {
085                            sb.append(file.getAbsolutePath());
086                            sb.append(File.pathSeparator);
087                    }
088    
089                    if (_log.isDebugEnabled()) {
090                            _log.debug("SPI embedded lib class path " + sb.toString());
091                    }
092    
093                    sb.append(contextPath);
094                    sb.append("/WEB-INF/classes");
095                    sb.append(File.pathSeparator);
096    
097                    sb.append(ClassPathUtil.getGlobalClassPath());
098    
099                    SPI_CLASS_PATH = sb.toString();
100    
101                    if (_log.isDebugEnabled()) {
102                            _log.debug("SPI class path " + SPI_CLASS_PATH);
103                    }
104    
105                    String spiProviderClassName = servletContext.getInitParameter(
106                            "spiProviderClassName");
107    
108                    Thread currentThread = Thread.currentThread();
109    
110                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
111    
112                    try {
113                            Class<SPIProvider> spiProviderClass =
114                                    (Class<SPIProvider>)loadClassDirectly(
115                                            contextClassLoader, spiProviderClassName);
116    
117                            SPIProvider spiProvider = spiProviderClass.newInstance();
118    
119                            boolean result = spiProviderReference.compareAndSet(
120                                    null, spiProvider);
121    
122                            if (!result) {
123                                    _log.error(
124                                            "Duplicate SPI provider " + spiProvider +
125                                                    " is already registered in servlet context " +
126                                                            servletContext.getContextPath());
127                            }
128                            else {
129                                    MPIHelperUtil.registerSPIProvider(spiProvider);
130                            }
131                    }
132                    catch (Exception e) {
133                            _log.error(
134                                    "Unable to create SPI provider with name " +
135                                            spiProviderClassName,
136                                    e);
137                    }
138            }
139    
140            protected static Class<?> loadClassDirectly(
141                            ClassLoader classLoader, String className)
142                    throws Exception {
143    
144                    synchronized (classLoader) {
145                            Method findLoadedClassMethod = ReflectionUtil.getDeclaredMethod(
146                                    ClassLoader.class, "findLoadedClass", String.class);
147    
148                            Class<?> clazz = (Class<?>)findLoadedClassMethod.invoke(
149                                    classLoader, className);
150    
151                            if (clazz == null) {
152                                    Method findClassMethod = ReflectionUtil.getDeclaredMethod(
153                                            ClassLoader.class, "findClass", String.class);
154    
155                                    clazz = (Class<?>)findClassMethod.invoke(
156                                            classLoader, className);
157                            }
158    
159                            Method resolveClassMethod = ReflectionUtil.getDeclaredMethod(
160                                    ClassLoader.class, "resolveClass", Class.class);
161    
162                            resolveClassMethod.invoke(classLoader, clazz);
163    
164                            return clazz;
165                    }
166            }
167    
168            protected static final AtomicReference<SPIProvider> spiProviderReference =
169                    new AtomicReference<SPIProvider>();
170    
171            private static Log _log = LogFactoryUtil.getLog(
172                    SPIClassPathContextListener.class);
173    
174    }