parent
cf8e667795
commit
d15ec4cdb4
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.liquibase;
|
||||
|
||||
import liquibase.servicelocator.CustomResolverServiceLocator;
|
||||
import liquibase.servicelocator.ServiceLocator;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationStartingEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} that replaces the liquibase {@link ServiceLocator} with a
|
||||
* version that works with Spring Boot executable archives.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LiquibaseServiceLocatorApplicationListener implements ApplicationListener<ApplicationStartingEvent> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LiquibaseServiceLocatorApplicationListener.class);
|
||||
|
||||
private static final boolean LIQUIBASE_PRESENT = ClassUtils.isPresent(
|
||||
"liquibase.servicelocator.CustomResolverServiceLocator",
|
||||
LiquibaseServiceLocatorApplicationListener.class.getClassLoader());
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationStartingEvent event) {
|
||||
if (LIQUIBASE_PRESENT) {
|
||||
new LiquibasePresent().replaceServiceLocator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to prevent class not found issues.
|
||||
*/
|
||||
private static class LiquibasePresent {
|
||||
|
||||
void replaceServiceLocator() {
|
||||
CustomResolverServiceLocator customResolverServiceLocator = new CustomResolverServiceLocator(
|
||||
new SpringPackageScanClassResolver(logger));
|
||||
ServiceLocator.setInstance(customResolverServiceLocator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.liquibase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import liquibase.servicelocator.DefaultPackageScanClassResolver;
|
||||
import liquibase.servicelocator.PackageScanClassResolver;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Liquibase {@link PackageScanClassResolver} implementation that uses Spring's resource
|
||||
* scanning to locate classes. This variant is safe to use with Spring Boot packaged
|
||||
* executable JARs.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SpringPackageScanClassResolver extends DefaultPackageScanClassResolver {
|
||||
|
||||
private final Log logger;
|
||||
|
||||
public SpringPackageScanClassResolver(Log logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void findAllClasses(String packageName, ClassLoader loader) {
|
||||
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(loader);
|
||||
try {
|
||||
Resource[] resources = scan(loader, packageName);
|
||||
for (Resource resource : resources) {
|
||||
Class<?> clazz = loadClass(loader, metadataReaderFactory, resource);
|
||||
if (clazz != null) {
|
||||
addFoundClass(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Resource[] scan(ClassLoader loader, String packageName) throws IOException {
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(loader);
|
||||
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
|
||||
+ ClassUtils.convertClassNameToResourcePath(packageName) + "/**/*.class";
|
||||
return resolver.getResources(pattern);
|
||||
}
|
||||
|
||||
private Class<?> loadClass(ClassLoader loader, MetadataReaderFactory readerFactory, Resource resource) {
|
||||
try {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
return ClassUtils.forName(reader.getClassMetadata().getClassName(), loader);
|
||||
}
|
||||
catch (ClassNotFoundException | LinkageError ex) {
|
||||
handleFailure(resource, ex);
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn("Unexpected failure when loading class resource " + resource, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFailure(Resource resource, Throwable ex) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Ignoring candidate class resource " + resource + " due to " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.liquibase;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import liquibase.servicelocator.CustomResolverServiceLocator;
|
||||
import liquibase.servicelocator.DefaultPackageScanClassResolver;
|
||||
import liquibase.servicelocator.ServiceLocator;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link LiquibaseServiceLocatorApplicationListener}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class LiquibaseServiceLocatorApplicationListenerTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacesServiceLocator() throws IllegalAccessException {
|
||||
SpringApplication application = new SpringApplication(Conf.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
this.context = application.run();
|
||||
Object resolver = getClassResolver();
|
||||
assertThat(resolver).isInstanceOf(SpringPackageScanClassResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceServiceLocatorBacksOffIfNotPresent() throws IllegalAccessException {
|
||||
SpringApplication application = new SpringApplication(Conf.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
resourceLoader.setClassLoader(new ClassHidingClassLoader(CustomResolverServiceLocator.class));
|
||||
application.setResourceLoader(resourceLoader);
|
||||
this.context = application.run();
|
||||
Object resolver = getClassResolver();
|
||||
assertThat(resolver).isInstanceOf(DefaultPackageScanClassResolver.class);
|
||||
}
|
||||
|
||||
private Object getClassResolver() throws IllegalAccessException {
|
||||
ServiceLocator instance = ServiceLocator.getInstance();
|
||||
Field field = ReflectionUtils.findField(ServiceLocator.class, "classResolver");
|
||||
field.setAccessible(true);
|
||||
return field.get(instance);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Conf {
|
||||
|
||||
}
|
||||
|
||||
private final class ClassHidingClassLoader extends URLClassLoader {
|
||||
|
||||
private final List<Class<?>> hiddenClasses;
|
||||
|
||||
private ClassHidingClassLoader(Class<?>... hiddenClasses) {
|
||||
super(new URL[0], LiquibaseServiceLocatorApplicationListenerTests.class.getClassLoader());
|
||||
this.hiddenClasses = Arrays.asList(hiddenClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
if (isHidden(name)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
private boolean isHidden(String name) {
|
||||
for (Class<?> hiddenClass : this.hiddenClasses) {
|
||||
if (hiddenClass.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.liquibase;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import liquibase.logging.Logger;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for SpringPackageScanClassResolver.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SpringPackageScanClassResolverTests {
|
||||
|
||||
@Test
|
||||
void testScan() {
|
||||
SpringPackageScanClassResolver resolver = new SpringPackageScanClassResolver(LogFactory.getLog(getClass()));
|
||||
resolver.addClassLoader(getClass().getClassLoader());
|
||||
Set<Class<?>> implementations = resolver.findImplementations(Logger.class, "liquibase.logging.core");
|
||||
assertThat(implementations).isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue