From 12f42602717ef4514701424c02960d5b150d43fa Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 11 Dec 2019 16:58:22 +0100 Subject: [PATCH] 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 --- ...tractSpringFunctionAdapterInitializer.java | 64 +-------- .../function/utils/FunctionClassUtils.java | 125 ++++++++++++++++++ 2 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java index 5e724ffdd..5c44d856b 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java @@ -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 implements Clo } public AbstractSpringFunctionAdapterInitializer() { - this(getStartClass()); + this(FunctionClassUtils.getStartClass()); } @Override @@ -264,62 +260,6 @@ public abstract class AbstractSpringFunctionAdapterInitializer 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 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 getAndInstrumentFromContext(String name) { this.functionRegistration = diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java new file mode 100644 index 000000000..26ee4e24c --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java @@ -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 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; + } +}