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:
Oleg Zhurakousky
2017-08-23 08:27:55 -04:00
committed by Dave Syer
parent 986e76610c
commit 37fc3e6c65
7 changed files with 43 additions and 123 deletions

View File

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

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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. * as a lookup mechanism for resolving types.
* *
* @author Andy Clement * @author Andy Clement
* @author Oleg Zhurakousky
*/ */
public class MemoryBasedJavaFileManager implements JavaFileManager { public class MemoryBasedJavaFileManager implements JavaFileManager {
@@ -55,7 +56,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
private List<CloseableFilterableJavaFileObjectIterable> toClose = new ArrayList<>(); private List<CloseableFilterableJavaFileObjectIterable> toClose = new ArrayList<>();
private Map<String,File> resolvedAdditionalDependencies = new LinkedHashMap<>(); private Map<String,File> resolvedAdditionalDependencies = new LinkedHashMap<>();
public MemoryBasedJavaFileManager() { public MemoryBasedJavaFileManager() {
outputCollector = new CompilationOutputCollector(); outputCollector = new CompilationOutputCollector();
} }
@@ -78,14 +79,11 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
public Iterable<JavaFileObject> list(Location location, String packageName, public Iterable<JavaFileObject> list(Location location, String packageName,
Set<Kind> kinds, boolean recurse) throws IOException { Set<Kind> kinds, boolean recurse) throws IOException {
logger.debug("list({},{},{},{})", location, packageName, kinds, recurse); logger.debug("list({},{},{},{})", location, packageName, kinds, recurse);
CloseableFilterableJavaFileObjectIterable resultIterable = null; String classpath = "";
if (location == StandardLocation.PLATFORM_CLASS_PATH if (location == StandardLocation.PLATFORM_CLASS_PATH
&& (kinds == null || kinds.contains(Kind.CLASS))) { && (kinds == null || kinds.contains(Kind.CLASS))) {
String sunBootClassPath = System.getProperty("sun.boot.class.path"); classpath = System.getProperty("sun.boot.class.path");
logger.debug("Creating iterable for boot class path: {}", sunBootClassPath); logger.debug("Creating iterable for boot class path: {}", classpath);
resultIterable = new IterableClasspath(sunBootClassPath, packageName,
recurse);
toClose.add(resultIterable);
} }
else if (location == StandardLocation.CLASS_PATH else if (location == StandardLocation.CLASS_PATH
&& (kinds == null || kinds.contains(Kind.CLASS))) { && (kinds == null || kinds.contains(Kind.CLASS))) {
@@ -95,18 +93,11 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency.toURI().toString().substring("file:".length()); javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency.toURI().toString().substring("file:".length());
} }
} }
logger.debug("Creating iterable for class path: {}", javaClassPath); classpath = javaClassPath;
resultIterable = new IterableClasspath(javaClassPath, packageName, recurse); logger.debug("Creating iterable for class path: {}", classpath);
toClose.add(resultIterable);
}
else if (location == StandardLocation.SOURCE_PATH) {
// There are no 'extra sources'
resultIterable = EmptyIterable.instance;
}
else {
// Nothing to list
resultIterable = EmptyIterable.instance;
} }
CloseableFilterableJavaFileObjectIterable resultIterable = new IterableClasspath(classpath, packageName, recurse);
toClose.add(resultIterable);
return resultIterable; return resultIterable;
} }
@@ -224,7 +215,7 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
try { try {
File resolved = engine.resolve(new Dependency(new DefaultArtifact(coordinates), "runtime")); File resolved = engine.resolve(new Dependency(new DefaultArtifact(coordinates), "runtime"));
// Example: // 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 // 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); resolvedAdditionalDependencies.put(dependency, resolved);
} catch (RuntimeException re) { } catch (RuntimeException re) {

View File

@@ -19,9 +19,9 @@ package org.springframework.cloud.function.compiler.proxy;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.springframework.asm.ClassReader;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.function.compiler.CompilationResultFactory; 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.compiler.java.SimpleClassLoader;
import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.cloud.function.support.FunctionFactoryMetadata;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@@ -30,36 +30,30 @@ import org.springframework.util.ReflectionUtils;
/** /**
* @author Mark Fisher * @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 Resource resource;
private final Class<?> type;
private CompilationResultFactory<T> factory;
private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader()); private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader());
private T target;
private Method method; private Method method;
public AbstractByteCodeLoadingProxy(Resource resource, Class<?> type) { AbstractByteCodeLoadingProxy(Resource resource) {
this.resource = resource; this.resource = resource;
this.type = type;
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream()); byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream());
String filename = this.resource.getFilename(); String className = new ClassReader(bytes).getClassName().replace("/", ".");
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());
Class<?> factoryClass = this.classLoader.defineClass(className, bytes); Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
try { try {
this.factory = (CompilationResultFactory<T>) factoryClass.newInstance(); this.target = ((CompilationResultFactory<T>) factoryClass.newInstance()).getResult();
this.method = findFactoryMethod(factoryClass); this.method = findFactoryMethod(factoryClass);
} }
catch (InstantiationException | IllegalAccessException e) { 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) { private Method findFactoryMethod(Class<?> clazz) {
AtomicReference<Method> method = new AtomicReference<>(); AtomicReference<Method> method = new AtomicReference<>();
ReflectionUtils.doWithLocalMethods(clazz, m -> { ReflectionUtils.doWithLocalMethods(clazz, m -> {
@@ -77,14 +81,4 @@ public abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBea
}); });
return method.get(); return method.get();
} }
@Override
public final T getTarget() {
return this.factory.getResult();
}
@Override
public Method getFactoryMethod() {
return this.method;
}
} }

View File

@@ -23,13 +23,14 @@ import org.springframework.core.io.Resource;
/** /**
* @author Mark Fisher * @author Mark Fisher
* @author Oleg Zhurakousky
* *
* @param <T> type * @param <T> type
*/ */
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>> implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> { public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>> implements FunctionFactoryMetadata<Consumer<T>>, Consumer<T> {
public ByteCodeLoadingConsumer(Resource resource) { public ByteCodeLoadingConsumer(Resource resource) {
super(resource, Consumer.class); super(resource);
} }
@Override @Override

View File

@@ -23,6 +23,7 @@ import org.springframework.core.io.Resource;
/** /**
* @author Mark Fisher * @author Mark Fisher
* @author Oleg Zhurakousky
* *
* @param <T> Function input type * @param <T> Function input type
* @param <R> Function result 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 class ByteCodeLoadingFunction<T, R> extends AbstractByteCodeLoadingProxy<Function<T, R>> implements FunctionFactoryMetadata<Function<T, R>>, Function<T, R> {
public ByteCodeLoadingFunction(Resource resource) { public ByteCodeLoadingFunction(Resource resource) {
super(resource, Function.class); super(resource);
} }
@Override @Override

View File

@@ -23,13 +23,14 @@ import org.springframework.core.io.Resource;
/** /**
* @author Mark Fisher * @author Mark Fisher
* @author Oleg Zhurakousky
* *
* @param <T> type * @param <T> type
*/ */
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>> implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> { public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>> implements FunctionFactoryMetadata<Supplier<T>>, Supplier<T> {
public ByteCodeLoadingSupplier(Resource resource) { public ByteCodeLoadingSupplier(Resource resource) {
super(resource, Supplier.class); super(resource);
} }
@Override @Override

View File

@@ -36,7 +36,7 @@ import reactor.core.publisher.Flux;
/** /**
* @author Dave Syer * @author Dave Syer
* * @author Oleg Zhurakousky
*/ */
public class ByteCodeLoadingFunctionTests { public class ByteCodeLoadingFunctionTests {
@@ -44,12 +44,7 @@ public class ByteCodeLoadingFunctionTests {
public void compileConsumer() throws Exception { public void compileConsumer() throws Exception {
CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>( CompiledFunctionFactory<Consumer<String>> compiled = new ConsumerCompiler<String>(
String.class.getName()).compile("foos", "System.out::println", "String"); String.class.getName()).compile("foos", "System.out::println", "String");
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") { ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
@Override
public String getFilename() {
return "foos.fun";
}
};
ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(resource); ByteCodeLoadingConsumer<String> consumer = new ByteCodeLoadingConsumer<>(resource);
consumer.afterPropertiesSet(); consumer.afterPropertiesSet();
assertThat(consumer instanceof FunctionFactoryMetadata); assertThat(consumer instanceof FunctionFactoryMetadata);
@@ -61,12 +56,7 @@ public class ByteCodeLoadingFunctionTests {
public void compileSupplier() throws Exception { public void compileSupplier() throws Exception {
CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>( CompiledFunctionFactory<Supplier<String>> compiled = new SupplierCompiler<String>(
String.class.getName()).compile("foos", "() -> \"foo\"", "String"); String.class.getName()).compile("foos", "() -> \"foo\"", "String");
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") { ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
@Override
public String getFilename() {
return "foos.fun";
}
};
ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(resource); ByteCodeLoadingSupplier<String> supplier = new ByteCodeLoadingSupplier<>(resource);
supplier.afterPropertiesSet(); supplier.afterPropertiesSet();
assertThat(supplier instanceof FunctionFactoryMetadata); assertThat(supplier instanceof FunctionFactoryMetadata);
@@ -78,12 +68,7 @@ public class ByteCodeLoadingFunctionTests {
public void compileFunction() throws Exception { public void compileFunction() throws Exception {
CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>( CompiledFunctionFactory<Function<String, String>> compiled = new FunctionCompiler<String, String>(
String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String"); String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String");
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") { ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
@Override
public String getFilename() {
return "foos.fun";
}
};
ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(resource); ByteCodeLoadingFunction<String, String> function = new ByteCodeLoadingFunction<>(resource);
function.afterPropertiesSet(); function.afterPropertiesSet();
assertThat(function instanceof FunctionFactoryMetadata); assertThat(function instanceof FunctionFactoryMetadata);
@@ -95,17 +80,11 @@ public class ByteCodeLoadingFunctionTests {
public void compileFluxFunction() throws Exception { public void compileFluxFunction() throws Exception {
CompiledFunctionFactory<Function<Flux<String>, Flux<String>>> compiled = new FunctionCompiler<Flux<String>, Flux<String>>( 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>"); String.class.getName()).compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux<String>", "Flux<String>");
ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos") { ByteArrayResource resource = new ByteArrayResource(compiled.getGeneratedClassBytes(), "foos");
@Override
public String getFilename() {
return "foos.fun";
}
};
ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(resource); ByteCodeLoadingFunction<Flux<String>, Flux<String>> function = new ByteCodeLoadingFunction<>(resource);
function.afterPropertiesSet(); function.afterPropertiesSet();
assertThat(function instanceof FunctionFactoryMetadata); assertThat(function instanceof FunctionFactoryMetadata);
assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isTrue(); assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isTrue();
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
} }
} }