Imprived class loading for new deployer

This commit is contained in:
Oleg Zhurakousky
2019-08-09 18:42:48 +02:00
parent 4e0e3fdc6d
commit 1248eaaa09
5 changed files with 109 additions and 15 deletions

View File

@@ -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);
}

View File

@@ -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<String, Object> functions = this.discoverFunctions();
if (logger.isInfoEnabled()) {
logger.info("Discovered functions: " + functions);
}
FunctionRegistry functionRegistry = deployerContext.getBean(FunctionRegistry.class);
for (Entry<String, Object> 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<Type> 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()) {

View File

@@ -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<? extends ApplicationContainer> applicationContainerCtr = (Constructor<? extends ApplicationContainer>) configurationClass
.getDeclaredConstructor(FunctionCatalog.class, FunctionInspector.class, FunctionProperties.class);

View File

@@ -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());
}
};
}
}