Imprived class loading for new deployer
This commit is contained in:
@@ -443,9 +443,17 @@ public class BeanFactoryAwareFunctionRegistry
|
|||||||
for (int i = 0; i < outputCount; i++) {
|
for (int i = 0; i < outputCount; i++) {
|
||||||
Expression parsed = new SpelExpressionParser().parseExpression("getT" + (i + 1) + "()");
|
Expression parsed = new SpelExpressionParser().parseExpression("getT" + (i + 1) + "()");
|
||||||
Object outputArgument = parsed.getValue(value);
|
Object outputArgument = parsed.getValue(value);
|
||||||
convertedInputArray[i] = outputArgument instanceof Publisher
|
try {
|
||||||
? this.convertOutputPublisherIfNecessary((Publisher<?>) outputArgument, acceptedOutputMimeTypes[i])
|
convertedInputArray[i] = outputArgument instanceof Publisher
|
||||||
: this.convertOutputValueIfNecessary(outputArgument, acceptedOutputMimeTypes);
|
? 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);
|
convertedValue = Tuples.fromArray(convertedInputArray);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import org.springframework.boot.loader.jar.JarFile;
|
|||||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||||
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
@@ -60,22 +59,24 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
|
|
||||||
private final Archive archive;
|
private final Archive archive;
|
||||||
|
|
||||||
|
private final boolean applicationWithMain;
|
||||||
|
|
||||||
ExternalFunctionJarLauncher(Archive archive) {
|
ExternalFunctionJarLauncher(Archive archive) {
|
||||||
super(archive);
|
super(archive);
|
||||||
this.archive = archive;
|
this.archive = archive;
|
||||||
|
this.applicationWithMain = this.isBootApplicationWithMain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
protected void deploy(ApplicationContext deployerContext, String[] args) {
|
protected void deploy(FunctionRegistry functionRegistry, FunctionProperties functionProperties, String[] args) {
|
||||||
|
|
||||||
ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
|
||||||
try {
|
try {
|
||||||
this.launch(deployerContext, args);
|
this.doLaunch(args);
|
||||||
Map<String, Object> functions = this.discoverFunctions();
|
Map<String, Object> functions = this.discoverFunctions();
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info("Discovered functions: " + functions);
|
logger.info("Discovered functions: " + functions);
|
||||||
}
|
}
|
||||||
FunctionRegistry functionRegistry = deployerContext.getBean(FunctionRegistry.class);
|
|
||||||
for (Entry<String, Object> entry : functions.entrySet()) {
|
for (Entry<String, Object> entry : functions.entrySet()) {
|
||||||
FunctionRegistration registration = new FunctionRegistration(entry.getValue(), entry.getKey());
|
FunctionRegistration registration = new FunctionRegistration(entry.getValue(), entry.getKey());
|
||||||
Type type = this.findType(entry.getKey());
|
Type type = this.findType(entry.getKey());
|
||||||
@@ -86,7 +87,7 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
registration.type(type);
|
registration.type(type);
|
||||||
functionRegistry.register(registration);
|
functionRegistry.register(registration);
|
||||||
}
|
}
|
||||||
FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(deployerContext.getBean(FunctionProperties.class));
|
FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionProperties);
|
||||||
if (registration != null) {
|
if (registration != null) {
|
||||||
functionRegistry.register(registration);
|
functionRegistry.register(registration);
|
||||||
}
|
}
|
||||||
@@ -109,11 +110,27 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
* While LaunchedURLClassLoader is completely disconnected with the current
|
* While LaunchedURLClassLoader is completely disconnected with the current
|
||||||
* class loader, this will still allow it to see FunctionContextUtils
|
* 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;
|
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
|
@Override
|
||||||
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("reactor.")) {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
if (!functionContextUtilsLoaded && className.equals(name)) {
|
if (!functionContextUtilsLoaded && className.equals(name)) {
|
||||||
Class<?> fcuClass = defineClass(name, fcuBytes, 0, fcuBytes.length);
|
Class<?> fcuClass = defineClass(name, fcuBytes, 0, fcuBytes.length);
|
||||||
this.functionContextUtilsLoaded = true;
|
this.functionContextUtilsLoaded = true;
|
||||||
@@ -128,6 +145,7 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
FunctionRegistration<?> functionRegistration = null;
|
FunctionRegistration<?> functionRegistration = null;
|
||||||
AtomicReference<Type> typeRef = new AtomicReference<>();
|
AtomicReference<Type> typeRef = new AtomicReference<>();
|
||||||
if (StringUtils.hasText(functionProperties.getFunctionClass())) {
|
if (StringUtils.hasText(functionProperties.getFunctionClass())) {
|
||||||
|
System.out.println("=====> " + Thread.currentThread().getContextClassLoader());
|
||||||
Class<?> functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionProperties.getFunctionClass());
|
Class<?> functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionProperties.getFunctionClass());
|
||||||
|
|
||||||
ReflectionUtils.doWithMethods(functionClass, new MethodCallback() {
|
ReflectionUtils.doWithMethods(functionClass, new MethodCallback() {
|
||||||
@@ -139,7 +157,8 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
@Override
|
@Override
|
||||||
public boolean matches(Method method) {
|
public boolean matches(Method method) {
|
||||||
String name = method.getName();
|
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;
|
return functionRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isBootApplicationWithMain() throws Exception {
|
protected boolean isBootApplicationWithMain() {
|
||||||
return StringUtils.hasText(this.archive.getManifest().getMainAttributes().getValue("Start-Class"));
|
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();
|
JarFile.registerUrlProtocolHandler();
|
||||||
Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives()));
|
Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives()));
|
||||||
|
System.out.println("=====> " + Thread.currentThread().getContextClassLoader());
|
||||||
evalContext.setTypeLocator(new StandardTypeLocator(Thread.currentThread().getContextClassLoader()));
|
evalContext.setTypeLocator(new StandardTypeLocator(Thread.currentThread().getContextClassLoader()));
|
||||||
|
|
||||||
if (this.isBootApplicationWithMain()) {
|
if (this.isBootApplicationWithMain()) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
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.cloud.function.context.catalog.FunctionInspector;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
@@ -63,7 +64,7 @@ public class FunctionDeployerBootstrap implements ApplicationContextAware {
|
|||||||
try {
|
try {
|
||||||
Archive archive = new JarFileArchive(new File(functionProperties.getLocation()));
|
Archive archive = new JarFileArchive(new File(functionProperties.getLocation()));
|
||||||
ExternalFunctionJarLauncher launcher = new ExternalFunctionJarLauncher(archive);
|
ExternalFunctionJarLauncher launcher = new ExternalFunctionJarLauncher(archive);
|
||||||
launcher.deploy(this.applicationContext, args);
|
launcher.deploy(this.applicationContext.getBean(FunctionRegistry.class), this.applicationContext.getBean(FunctionProperties.class), args);
|
||||||
|
|
||||||
Constructor<? extends ApplicationContainer> applicationContainerCtr = (Constructor<? extends ApplicationContainer>) configurationClass
|
Constructor<? extends ApplicationContainer> applicationContainerCtr = (Constructor<? extends ApplicationContainer>) configurationClass
|
||||||
.getDeclaredConstructor(FunctionCatalog.class, FunctionInspector.class, FunctionProperties.class);
|
.getDeclaredConstructor(FunctionCatalog.class, FunctionInspector.class, FunctionProperties.class);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user