Removed EmptyIterable
- Removed EmptyIterable as it's only used by _MemoryBasedJavaFileManager_ to ensure the contract of the _list(..)_ operation that must not return null. The same contract is ensured with _new IterableClasspath(classpath, packageName, recurse)_ while making _MemoryBasedJavaFileManager.list(..)_ simpler and more consistent. - Untill this fix the AbstractByteCodeLoadingProxy was building FQCN of the byte-array defined class using Resource.getFileName() and then some, which is not very reliable since if such name does not match the actual name contained in the byte code, class loading will result in exception. So, this fix reads FQCN from the actual byte code.. - Reduced visibility of AbstractByteCodeLoadingProxy - Simplified ByteCodeLoadingFunctionTests Closes gh-99
This commit is contained in:
committed by
Dave Syer
parent
986e76610c
commit
37fc3e6c65
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.compiler.java;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import org.apache.commons.collections.IteratorUtils;
|
||||
|
||||
/**
|
||||
* Simple iterable that can be used to return an iterator over no values.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
class EmptyIterable extends CloseableFilterableJavaFileObjectIterable {
|
||||
|
||||
static EmptyIterable instance = new EmptyIterable();
|
||||
|
||||
private EmptyIterable() {
|
||||
super(null,false);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Iterator<JavaFileObject> iterator() {
|
||||
return IteratorUtils.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
*
|
||||
* http://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.
|
||||
@@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory;
|
||||
* as a lookup mechanism for resolving types.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
|
||||
@@ -55,7 +56,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
private List<CloseableFilterableJavaFileObjectIterable> toClose = new ArrayList<>();
|
||||
|
||||
private Map<String,File> resolvedAdditionalDependencies = new LinkedHashMap<>();
|
||||
|
||||
|
||||
public MemoryBasedJavaFileManager() {
|
||||
outputCollector = new CompilationOutputCollector();
|
||||
}
|
||||
@@ -78,14 +79,11 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
public Iterable<JavaFileObject> list(Location location, String packageName,
|
||||
Set<Kind> kinds, boolean recurse) throws IOException {
|
||||
logger.debug("list({},{},{},{})", location, packageName, kinds, recurse);
|
||||
CloseableFilterableJavaFileObjectIterable resultIterable = null;
|
||||
String classpath = "";
|
||||
if (location == StandardLocation.PLATFORM_CLASS_PATH
|
||||
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||
String sunBootClassPath = System.getProperty("sun.boot.class.path");
|
||||
logger.debug("Creating iterable for boot class path: {}", sunBootClassPath);
|
||||
resultIterable = new IterableClasspath(sunBootClassPath, packageName,
|
||||
recurse);
|
||||
toClose.add(resultIterable);
|
||||
classpath = System.getProperty("sun.boot.class.path");
|
||||
logger.debug("Creating iterable for boot class path: {}", classpath);
|
||||
}
|
||||
else if (location == StandardLocation.CLASS_PATH
|
||||
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||
@@ -95,18 +93,11 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency.toURI().toString().substring("file:".length());
|
||||
}
|
||||
}
|
||||
logger.debug("Creating iterable for class path: {}", javaClassPath);
|
||||
resultIterable = new IterableClasspath(javaClassPath, packageName, recurse);
|
||||
toClose.add(resultIterable);
|
||||
}
|
||||
else if (location == StandardLocation.SOURCE_PATH) {
|
||||
// There are no 'extra sources'
|
||||
resultIterable = EmptyIterable.instance;
|
||||
}
|
||||
else {
|
||||
// Nothing to list
|
||||
resultIterable = EmptyIterable.instance;
|
||||
classpath = javaClassPath;
|
||||
logger.debug("Creating iterable for class path: {}", classpath);
|
||||
}
|
||||
CloseableFilterableJavaFileObjectIterable resultIterable = new IterableClasspath(classpath, packageName, recurse);
|
||||
toClose.add(resultIterable);
|
||||
return resultIterable;
|
||||
}
|
||||
|
||||
@@ -224,7 +215,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
try {
|
||||
File resolved = engine.resolve(new Dependency(new DefaultArtifact(coordinates), "runtime"));
|
||||
// Example:
|
||||
// dependency = maven://org.springframework:spring-expression:4.3.9.RELEASE
|
||||
// dependency = maven://org.springframework:spring-expression:4.3.9.RELEASE
|
||||
// resolved.toURI() = file:/Users/aclement/.m2/repository/org/springframework/spring-expression/4.3.9.RELEASE/spring-expression-4.3.9.RELEASE.jar
|
||||
resolvedAdditionalDependencies.put(dependency, resolved);
|
||||
} catch (RuntimeException re) {
|
||||
|
||||
@@ -19,9 +19,9 @@ package org.springframework.cloud.function.compiler.proxy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.function.compiler.CompilationResultFactory;
|
||||
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||
import org.springframework.cloud.function.compiler.java.SimpleClassLoader;
|
||||
import org.springframework.cloud.function.support.FunctionFactoryMetadata;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -30,36 +30,30 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean, FunctionFactoryMetadata<T> {
|
||||
abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean, FunctionFactoryMetadata<T> {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private CompilationResultFactory<T> factory;
|
||||
|
||||
private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader());
|
||||
|
||||
private T target;
|
||||
|
||||
private Method method;
|
||||
|
||||
public AbstractByteCodeLoadingProxy(Resource resource, Class<?> type) {
|
||||
AbstractByteCodeLoadingProxy(Resource resource) {
|
||||
this.resource = resource;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream());
|
||||
String filename = this.resource.getFilename();
|
||||
String functionName = filename == null ? type.getSimpleName() : filename.replaceAll(".fun$", "");
|
||||
String firstLetter = functionName.substring(0, 1).toUpperCase();
|
||||
String upperCasedName = (functionName.length() > 1) ? firstLetter + functionName.substring(1) : firstLetter;
|
||||
String className = String.format("%s.%s%sFactory", FunctionCompiler.class.getPackage().getName(), upperCasedName, this.type.getSimpleName());
|
||||
String className = new ClassReader(bytes).getClassName().replace("/", ".");
|
||||
Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
|
||||
try {
|
||||
this.factory = (CompilationResultFactory<T>) factoryClass.newInstance();
|
||||
this.target = ((CompilationResultFactory<T>) factoryClass.newInstance()).getResult();
|
||||
this.method = findFactoryMethod(factoryClass);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
@@ -67,6 +61,16 @@ public abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBea
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getFactoryMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
private Method findFactoryMethod(Class<?> clazz) {
|
||||
AtomicReference<Method> method = new AtomicReference<>();
|
||||
ReflectionUtils.doWithLocalMethods(clazz, m -> {
|
||||
@@ -77,14 +81,4 @@ public abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBea
|
||||
});
|
||||
return method.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getTarget() {
|
||||
return this.factory.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getFactoryMethod() {
|
||||
return this.method;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
* @param <T> type
|
||||
*/
|
||||
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>> implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> {
|
||||
|
||||
public ByteCodeLoadingConsumer(Resource resource) {
|
||||
super(resource, Consumer.class);
|
||||
super(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
* @param <T> Function input type
|
||||
* @param <R> Function result type
|
||||
@@ -30,7 +31,7 @@ import org.springframework.core.io.Resource;
|
||||
public class ByteCodeLoadingFunction<T, R> extends AbstractByteCodeLoadingProxy<Function<T, R>> implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
|
||||
|
||||
public ByteCodeLoadingFunction(Resource resource) {
|
||||
super(resource, Function.class);
|
||||
super(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,13 +23,14 @@ import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
* @param <T> type
|
||||
*/
|
||||
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>> implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
|
||||
|
||||
public ByteCodeLoadingSupplier(Resource resource) {
|
||||
super(resource, Supplier.class);
|
||||
super(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,7 +36,7 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public class ByteCodeLoadingFunctionTests {
|
||||
|
||||
@@ -44,12 +44,7 @@ public class ByteCodeLoadingFunctionTests {
|
||||
public void compileConsumer() throws Exception {
|
||||
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
|
||||
String.class.getName()).compile("foos", "System.out::println", "String");
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "foos.fun";
|
||||
}
|
||||
};
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
||||
ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(resource);
|
||||
consumer.afterPropertiesSet();
|
||||
assertThat(consumer instanceof FunctionFactoryMetadata);
|
||||
@@ -61,12 +56,7 @@ public class ByteCodeLoadingFunctionTests {
|
||||
public void compileSupplier() throws Exception {
|
||||
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
|
||||
String.class.getName()).compile("foos", "() -> \"foo\"", "String");
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "foos.fun";
|
||||
}
|
||||
};
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
||||
ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(resource);
|
||||
supplier.afterPropertiesSet();
|
||||
assertThat(supplier instanceof FunctionFactoryMetadata);
|
||||
@@ -78,12 +68,7 @@ public class ByteCodeLoadingFunctionTests {
|
||||
public void compileFunction() throws Exception {
|
||||
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
|
||||
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String");
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "foos.fun";
|
||||
}
|
||||
};
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
||||
ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(resource);
|
||||
function.afterPropertiesSet();
|
||||
assertThat(function instanceof FunctionFactoryMetadata);
|
||||
@@ -95,17 +80,11 @@ public class ByteCodeLoadingFunctionTests {
|
||||
public void compileFluxFunction() throws Exception {
|
||||
CompiledFunctionFactory<Function<Flux<String>, Flux<String>>> compiled = new FunctionCompiler<Flux<String>, Flux<String>>(
|
||||
String.class.getName()).compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "foos.fun";
|
||||
}
|
||||
};
|
||||
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
|
||||
ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(resource);
|
||||
function.afterPropertiesSet();
|
||||
assertThat(function instanceof FunctionFactoryMetadata);
|
||||
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isTrue();
|
||||
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user