GH-421 Fix Start-Class discovery logic

- Fixed logic behind discovery of Start-Class which didn't work well with some JVMs
- Isolated the Start-Class in a separate utility class as it is going to be used elsewhere

Resolves #421
This commit is contained in:
Oleg Zhurakousky
2019-12-11 16:58:22 +01:00
parent a3a08919e0
commit 12f4260271
2 changed files with 127 additions and 62 deletions

View File

@@ -17,18 +17,14 @@
package org.springframework.cloud.function.context;
import java.io.Closeable;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,11 +39,11 @@ import org.springframework.boot.WebApplicationType;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.context.config.FunctionContextUtils;
import org.springframework.cloud.function.context.config.RoutingFunction;
import org.springframework.cloud.function.utils.FunctionClassUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
@@ -97,7 +93,7 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> implements Clo
}
public AbstractSpringFunctionAdapterInitializer() {
this(getStartClass());
this(FunctionClassUtils.getStartClass());
}
@Override
@@ -264,62 +260,6 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> implements Clo
throw new IllegalStateException("Unknown type " + type);
}
private static Class<?> getStartClass() {
ClassLoader classLoader = AbstractSpringFunctionAdapterInitializer.class.getClassLoader();
Class<?> mainClass = null;
if (System.getenv("MAIN_CLASS") != null) {
mainClass = ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader);
}
else if (System.getProperty("MAIN_CLASS") != null) {
mainClass = ClassUtils.resolveClassName(System.getProperty("MAIN_CLASS"), classLoader);
}
else {
try {
Class<?> result = getStartClass(
Collections.list(classLoader.getResources("META-INF/MANIFEST.MF")));
if (result == null) {
result = getStartClass(Collections
.list(classLoader.getResources("meta-inf/manifest.mf")));
}
Assert.notNull(result, "Failed to locate main class");
mainClass = result;
}
catch (Exception ex) {
throw new IllegalStateException("Failed to discover main class. An attempt was made to discover "
+ "main class as 'MAIN_CLASS' environment variable, system property as well as "
+ "entry in META-INF/MANIFEST.MF (in that order).", ex);
}
}
logger.info("Main class: " + mainClass);
return mainClass;
}
private static Class<?> getStartClass(List<URL> list) {
logger.info("Searching manifests: " + list);
for (URL url : list) {
try {
logger.info("Searching manifest: " + url);
InputStream inputStream = url.openStream();
try {
Manifest manifest = new Manifest(inputStream);
String startClass = manifest.getMainAttributes()
.getValue("Start-Class");
if (startClass != null) {
return ClassUtils.forName(startClass,
AbstractSpringFunctionAdapterInitializer.class.getClassLoader());
}
}
finally {
inputStream.close();
}
}
catch (Exception ex) {
}
}
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> T getAndInstrumentFromContext(String name) {
this.functionRegistration =

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2019-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.cloud.function.utils;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* General utility class which aggregates various class-level utility functions
* used by the framework.
*
* @author Oleg Zhurakousky
* @since 3.0.1
*/
public final class FunctionClassUtils {
private static Log logger = LogFactory.getLog(FunctionClassUtils.class);
private FunctionClassUtils() {
}
/**
* Discovers the start class in the currently running application.
* The discover search order is 'MAIN_CLASS' environment property,
* 'MAIN_CLASS' system property, META-INF/MANIFEST.MF:'Start-Class' attribute,
* meta-inf/manifest.mf:'Start-Class' attribute.
*
* @return instance of Class which represent the start class of the application.
*/
public static Class<?> getStartClass() {
ClassLoader classLoader = FunctionClassUtils.class.getClassLoader();
return getStartClass(classLoader);
}
private static Class<?> getStartClass(ClassLoader classLoader) {
Class<?> mainClass = null;
if (System.getenv("MAIN_CLASS") != null) {
mainClass = ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader);
}
else if (System.getProperty("MAIN_CLASS") != null) {
mainClass = ClassUtils.resolveClassName(System.getProperty("MAIN_CLASS"), classLoader);
}
else {
try {
Class<?> result = getStartClass(
Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)));
if (result == null) {
result = getStartClass(Collections
.list(classLoader.getResources("meta-inf/manifest.mf")));
}
Assert.notNull(result, "Failed to locate main class");
mainClass = result;
}
catch (Exception ex) {
throw new IllegalStateException("Failed to discover main class. An attempt was made to discover "
+ "main class as 'MAIN_CLASS' environment variable, system property as well as "
+ "entry in META-INF/MANIFEST.MF (in that order).", ex);
}
}
logger.info("Main class: " + mainClass);
return mainClass;
}
private static Class<?> getStartClass(List<URL> list) {
logger.info("Searching manifests: " + list);
for (URL url : list) {
try {
logger.info("Searching manifest: " + url);
Manifest manifest;
InputStream inputStream = null;
try {
if ("jar".equals(url.getProtocol())) {
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
manifest = jarConnection.getManifest();
}
else {
manifest = new Manifest(url.openStream());
}
}
finally {
if (inputStream != null) {
inputStream.close();
}
}
String startClass = manifest.getMainAttributes().getValue("Start-Class");
if (startClass != null) {
return ClassUtils.forName(startClass, FunctionClassUtils.class.getClassLoader());
}
}
catch (Exception ex) {
logger.debug("Failed to determine Start-Class in manifest file of " + url, ex);
}
}
return null;
}
}