/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.util.resiliency.spi.provider;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
import com.liferay.portal.kernel.resiliency.spi.SPIUtil;
import com.liferay.portal.kernel.resiliency.spi.provider.SPIProvider;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.ClassUtil;
import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.ReflectionUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* @author Shuyang Zhou
*/
public class SPIClassPathContextListener implements ServletContextListener {
public static volatile String SPI_CLASS_PATH = StringPool.BLANK;
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
SPIProvider spiProvider = spiProviderReference.getAndSet(null);
if (spiProvider != null) {
MPIHelperUtil.unregisterSPIProvider(spiProvider);
}
}
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
String contextPath = servletContext.getRealPath(StringPool.BLANK);
String spiEmbeddedLibDirName = servletContext.getInitParameter(
"spiEmbeddedLibDir");
Set<File> jarFiles = new LinkedHashSet<>();
// Load embedded Tomcat
File spiEmbeddedLibDir = new File(contextPath, spiEmbeddedLibDirName);
addJarFiles(jarFiles, spiEmbeddedLibDir);
// Load portal-kernel.jar from MPI
addJarFiles(
jarFiles, PortalClassLoaderUtil.getClassLoader(),
PortalException.class.getName());
// Load JDBC driver jars from MPI
addJarFiles(
jarFiles, PortalClassLoaderUtil.getClassLoader(),
PropsUtil.get(PropsKeys.JDBC_DEFAULT_DRIVER_CLASS_NAME));
// Load ext jars
addJarFiles(jarFiles, new File(spiEmbeddedLibDir, "ext"));
StringBundler sb = new StringBundler(jarFiles.size() * 2 + 2);
for (File file : jarFiles) {
sb.append(file.getAbsolutePath());
sb.append(File.pathSeparator);
}
sb.append(contextPath);
sb.append("/WEB-INF/classes");
SPI_CLASS_PATH = sb.toString();
if (_log.isDebugEnabled()) {
_log.debug("SPI class path " + SPI_CLASS_PATH);
}
String spiProviderClassName = servletContext.getInitParameter(
"spiProviderClassName");
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader = currentThread.getContextClassLoader();
try {
Class<SPIProvider> spiProviderClass = null;
if (SPIUtil.isSPI()) {
spiProviderClass = (Class<SPIProvider>)loadClassDirectly(
contextClassLoader, spiProviderClassName);
}
else {
spiProviderClass =
(Class<SPIProvider>)contextClassLoader.loadClass(
spiProviderClassName);
}
SPIProvider spiProvider = spiProviderClass.newInstance();
boolean result = spiProviderReference.compareAndSet(
null, spiProvider);
if (!result) {
_log.error(
"Duplicate SPI provider " + spiProvider +
" is already registered in servlet context " +
servletContext.getContextPath());
}
else {
MPIHelperUtil.registerSPIProvider(spiProvider);
}
}
catch (Exception e) {
_log.error(
"Unable to create SPI provider with name " +
spiProviderClassName,
e);
}
}
protected static Class<?> loadClassDirectly(
ClassLoader classLoader, String className)
throws Exception {
synchronized (classLoader) {
Method findLoadedClassMethod = ReflectionUtil.getDeclaredMethod(
ClassLoader.class, "findLoadedClass", String.class);
Class<?> clazz = (Class<?>)findLoadedClassMethod.invoke(
classLoader, className);
if (clazz == null) {
Method findClassMethod = ReflectionUtil.getDeclaredMethod(
ClassLoader.class, "findClass", String.class);
clazz = (Class<?>)findClassMethod.invoke(
classLoader, className);
}
Method resolveClassMethod = ReflectionUtil.getDeclaredMethod(
ClassLoader.class, "resolveClass", Class.class);
resolveClassMethod.invoke(classLoader, clazz);
return clazz;
}
}
protected void addJarFiles(
Set<File> jarFiles, ClassLoader classLoader, String className) {
String path = ClassUtil.getParentPath(classLoader, className);
int pos = path.lastIndexOf(".jar");
pos = path.lastIndexOf(CharPool.SLASH, pos);
path = path.substring(0, pos);
addJarFiles(jarFiles, new File(path));
}
protected void addJarFiles(Set<File> jarFiles, File dir) {
if (!dir.exists() || !dir.isDirectory()) {
throw new RuntimeException(
"Unable to find directory " + dir.getAbsolutePath());
}
File[] files = dir.listFiles();
Arrays.sort(files);
for (File file : files) {
String fileName = file.getName();
if (fileName.endsWith(".jar")) {
jarFiles.add(file);
}
}
}
protected static final AtomicReference<SPIProvider> spiProviderReference =
new AtomicReference<>();
private static final Log _log = LogFactoryUtil.getLog(
SPIClassPathContextListener.class);
}