From 1248eaaa091d19f6ef79d43ce13007e883169d4e Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 9 Aug 2019 18:42:48 +0200 Subject: [PATCH] Imprived class loading for new deployer --- .../BeanFactoryAwareFunctionRegistry.java | 14 ++++- .../deployer/ExternalFunctionJarLauncher.java | 46 ++++++++++---- .../deployer/FunctionDeployerBootstrap.java | 3 +- .../FunctionDeployerConfiguration.java | 60 +++++++++++++++++++ .../src/main/resources/application.properties | 1 - 5 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java delete mode 100644 spring-cloud-function-deployer-new/src/main/resources/application.properties diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java index 3e84b515a..55fe12771 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java @@ -443,9 +443,17 @@ public class BeanFactoryAwareFunctionRegistry for (int i = 0; i < outputCount; i++) { Expression parsed = new SpelExpressionParser().parseExpression("getT" + (i + 1) + "()"); Object outputArgument = parsed.getValue(value); - convertedInputArray[i] = outputArgument instanceof Publisher - ? this.convertOutputPublisherIfNecessary((Publisher) outputArgument, acceptedOutputMimeTypes[i]) - : this.convertOutputValueIfNecessary(outputArgument, acceptedOutputMimeTypes); + try { + convertedInputArray[i] = outputArgument instanceof Publisher + ? this.convertOutputPublisherIfNecessary((Publisher) outputArgument, acceptedOutputMimeTypes[i]) + : this.convertOutputValueIfNecessary(outputArgument, acceptedOutputMimeTypes); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalStateException("The number of 'acceptedOutputMimeTypes' for function '" + this.functionDefinition + + "' is (" + acceptedOutputMimeTypes.length + + "), which does not match the number of actual outputs of this function which is (" + outputCount + ").", e); + } + } convertedValue = Tuples.fromArray(convertedInputArray); } diff --git a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ExternalFunctionJarLauncher.java b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ExternalFunctionJarLauncher.java index 40253b62c..ac0130af5 100644 --- a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ExternalFunctionJarLauncher.java +++ b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ExternalFunctionJarLauncher.java @@ -35,7 +35,6 @@ import org.springframework.boot.loader.jar.JarFile; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -60,22 +59,24 @@ class ExternalFunctionJarLauncher extends JarLauncher { private final Archive archive; + private final boolean applicationWithMain; + ExternalFunctionJarLauncher(Archive archive) { super(archive); this.archive = archive; + this.applicationWithMain = this.isBootApplicationWithMain(); } @SuppressWarnings({ "unchecked", "rawtypes" }) - protected void deploy(ApplicationContext deployerContext, String[] args) { + protected void deploy(FunctionRegistry functionRegistry, FunctionProperties functionProperties, String[] args) { ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); try { - this.launch(deployerContext, args); + this.doLaunch(args); Map functions = this.discoverFunctions(); if (logger.isInfoEnabled()) { logger.info("Discovered functions: " + functions); } - FunctionRegistry functionRegistry = deployerContext.getBean(FunctionRegistry.class); for (Entry entry : functions.entrySet()) { FunctionRegistration registration = new FunctionRegistration(entry.getValue(), entry.getKey()); Type type = this.findType(entry.getKey()); @@ -86,7 +87,7 @@ class ExternalFunctionJarLauncher extends JarLauncher { registration.type(type); functionRegistry.register(registration); } - FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(deployerContext.getBean(FunctionProperties.class)); + FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionProperties); if (registration != null) { functionRegistry.register(registration); } @@ -109,11 +110,27 @@ class ExternalFunctionJarLauncher extends JarLauncher { * While LaunchedURLClassLoader is completely disconnected with the current * class loader, this will still allow it to see FunctionContextUtils */ - return new ClassLoader(new LaunchedURLClassLoader(urls, getClass().getClassLoader().getParent())) { + return new LaunchedURLClassLoader(urls, getClass().getClassLoader().getParent()) { boolean functionContextUtilsLoaded; + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (!ExternalFunctionJarLauncher.this.applicationWithMain) { + try { + return getClass().getClassLoader().loadClass(name); + } + catch (Exception e) { + //ignore and proceed with context ClassLoader + } + } + return super.loadClass(name, false); + } + @Override protected Class findClass(final String name) throws ClassNotFoundException { + if (name.startsWith("reactor.")) { + System.out.println(); + } if (!functionContextUtilsLoaded && className.equals(name)) { Class fcuClass = defineClass(name, fcuBytes, 0, fcuBytes.length); this.functionContextUtilsLoaded = true; @@ -128,6 +145,7 @@ class ExternalFunctionJarLauncher extends JarLauncher { FunctionRegistration functionRegistration = null; AtomicReference typeRef = new AtomicReference<>(); if (StringUtils.hasText(functionProperties.getFunctionClass())) { + System.out.println("=====> " + Thread.currentThread().getContextClassLoader()); Class functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionProperties.getFunctionClass()); ReflectionUtils.doWithMethods(functionClass, new MethodCallback() { @@ -139,7 +157,8 @@ class ExternalFunctionJarLauncher extends JarLauncher { @Override public boolean matches(Method method) { String name = method.getName(); - return typeRef.get() == null && ("apply".equals(name) || "accept".equals(name) || "get".equals(name)); + return typeRef.get() == null && !method.isBridge() + && ("apply".equals(name) || "accept".equals(name) || "get".equals(name)); } }); @@ -154,13 +173,20 @@ class ExternalFunctionJarLauncher extends JarLauncher { return functionRegistration; } - protected boolean isBootApplicationWithMain() throws Exception { - return StringUtils.hasText(this.archive.getManifest().getMainAttributes().getValue("Start-Class")); + protected boolean isBootApplicationWithMain() { + try { + return StringUtils.hasText(this.archive.getManifest().getMainAttributes().getValue("Start-Class")); + } + catch (Exception e) { + throw new IllegalStateException(e); + } + } - private void launch(ApplicationContext deployerContext, String[] args) throws Exception { + private void doLaunch(String[] args) throws Exception { JarFile.registerUrlProtocolHandler(); Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives())); + System.out.println("=====> " + Thread.currentThread().getContextClassLoader()); evalContext.setTypeLocator(new StandardTypeLocator(Thread.currentThread().getContextClassLoader())); if (this.isBootApplicationWithMain()) { diff --git a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerBootstrap.java b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerBootstrap.java index a985bf5b1..d7474d422 100644 --- a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerBootstrap.java +++ b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerBootstrap.java @@ -27,6 +27,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.JarFileArchive; import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.function.context.FunctionRegistry; import org.springframework.cloud.function.context.catalog.FunctionInspector; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -63,7 +64,7 @@ public class FunctionDeployerBootstrap implements ApplicationContextAware { try { Archive archive = new JarFileArchive(new File(functionProperties.getLocation())); ExternalFunctionJarLauncher launcher = new ExternalFunctionJarLauncher(archive); - launcher.deploy(this.applicationContext, args); + launcher.deploy(this.applicationContext.getBean(FunctionRegistry.class), this.applicationContext.getBean(FunctionProperties.class), args); Constructor applicationContainerCtr = (Constructor) configurationClass .getDeclaredConstructor(FunctionCatalog.class, FunctionInspector.class, FunctionProperties.class); diff --git a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java new file mode 100644 index 000000000..04784e308 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2017-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.deployer; + +import java.io.File; +import java.io.IOException; + +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.loader.archive.Archive; +import org.springframework.boot.loader.archive.JarFileArchive; +import org.springframework.cloud.function.context.FunctionRegistry; +import org.springframework.context.annotation.Bean; + +/** + * + * @author Oleg Zhurakousky + * + * @since 3.0 + * + */ +@EnableAutoConfiguration +@EnableConfigurationProperties(FunctionProperties.class) +public class FunctionDeployerConfiguration { + + @Bean + public SmartInitializingSingleton functionDeployer(FunctionProperties functionProperties, + FunctionRegistry functionRegistry, ApplicationArguments arguments) { + return new SmartInitializingSingleton() { + @Override + public void afterSingletonsInstantiated() { + Archive archive = null; + try { + archive = new JarFileArchive(new File(functionProperties.getLocation())); + } + catch (IOException e) { + throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation(), e); + } + ExternalFunctionJarLauncher launcher = new ExternalFunctionJarLauncher(archive); + launcher.deploy(functionRegistry, functionProperties, arguments.getSourceArgs()); + } + }; + } +} diff --git a/spring-cloud-function-deployer-new/src/main/resources/application.properties b/spring-cloud-function-deployer-new/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-deployer-new/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -