Support for isolated class loaders extended to cover more functions

Functions with Flux and Message (as well as POJOs and Flux of POJO
which were already supported) should now work if they are created in
an isolated class loader. Preconditions:

* The class loaders must have the reactor-core (and reactive-streams)
shared between the app and the function. Practically speaking this means
there has to be a parent class loader with just reactive types, and
sibling children for the app and the function. This is not a new
requirement (it was needed for Flux of POJO anyway).

* Message types are handled reflectively, so they don't have to be in a
shared class loader. But they do have to be  on the class path on
both sides (obviously).
This commit is contained in:
Dave Syer
2018-02-16 08:16:55 +00:00
parent ccd3953163
commit 1b624c3531
17 changed files with 615 additions and 33 deletions

View File

@@ -28,7 +28,7 @@ import reactor.core.publisher.Flux;
*
* @param <T> input type of target consumer
*/
public class FluxConsumer<T> implements Consumer<Flux<T>> {
public class FluxConsumer<T> implements Consumer<Flux<T>>, FluxWrapper<Consumer<T>> {
private final Consumer<T> consumer;
@@ -36,6 +36,11 @@ public class FluxConsumer<T> implements Consumer<Flux<T>> {
this.consumer = consumer;
}
@Override
public Consumer<T> getTarget() {
return this.consumer;
}
@Override
public void accept(Flux<T> input) {
input.subscribe(t -> consumer.accept(t));

View File

@@ -29,13 +29,18 @@ import reactor.core.publisher.Flux;
* @param <T> input type of target function
* @param <R> output type of target function
*/
public class FluxFunction<T, R> implements Function<Flux<T>, Flux<R>> {
public class FluxFunction<T, R> implements Function<Flux<T>, Flux<R>>, FluxWrapper<Function<T, R>> {
private final Function<T, R> function;
public FluxFunction(Function<T, R> function) {
this.function = function;
}
@Override
public Function<T, R> getTarget() {
return this.function;
}
@Override
public Flux<R> apply(Flux<T> input) {

View File

@@ -33,7 +33,7 @@ import reactor.core.publisher.Flux;
*
* @param <T> output type of target supplier
*/
public class FluxSupplier<T> implements Supplier<Flux<T>> {
public class FluxSupplier<T> implements Supplier<Flux<T>>, FluxWrapper<Supplier<T>> {
private final Supplier<T> supplier;
@@ -42,12 +42,17 @@ public class FluxSupplier<T> implements Supplier<Flux<T>> {
public FluxSupplier(Supplier<T> supplier) {
this(supplier, null);
}
public FluxSupplier(Supplier<T> supplier, Duration period) {
this.supplier = supplier;
this.period = period;
}
@Override
public Supplier<T> getTarget() {
return this.supplier;
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Flux<T> get() {

View File

@@ -0,0 +1,27 @@
/*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.function.core;
/**
* @author Dave Syer
*
*/
public interface FluxWrapper<T> {
T getTarget();
}

View File

@@ -0,0 +1,26 @@
/*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.function.core;
/**
* @author Dave Syer
*
*/
public interface Isolated {
ClassLoader getClassLoader();
}

View File

@@ -24,18 +24,25 @@ import org.springframework.util.ClassUtils;
* @author Dave Syer
*
*/
public class IsolatedConsumer<T> implements Consumer<T> {
public class IsolatedConsumer<T> implements Consumer<T>, Isolated {
private final Consumer<T> consumer;
private final ClassLoader classLoader;
public IsolatedConsumer(Consumer<T> consumer) {
this.consumer = consumer;
this.classLoader = consumer.getClass().getClassLoader();
}
@Override
public ClassLoader getClassLoader() {
return this.classLoader;
}
@Override
public void accept(T item) {
ClassLoader context = ClassUtils
.overrideThreadContextClassLoader(consumer.getClass().getClassLoader());
.overrideThreadContextClassLoader(this.classLoader);
try {
consumer.accept(item);
}

View File

@@ -24,20 +24,27 @@ import org.springframework.util.ClassUtils;
* @author Dave Syer
*
*/
public class IsolatedFunction<S, T> implements Function<S, T> {
public class IsolatedFunction<S, T> implements Function<S, T>, Isolated {
private final Function<S, T> function;
private final ClassLoader classLoader;
public IsolatedFunction(Function<S, T> function) {
this.function = function;
this.classLoader = function.getClass().getClassLoader();
}
@Override
public ClassLoader getClassLoader() {
return this.classLoader;
}
@Override
public T apply(S item) {
ClassLoader context = ClassUtils
.overrideThreadContextClassLoader(function.getClass().getClassLoader());
.overrideThreadContextClassLoader(this.classLoader);
try {
return function.apply(item);
return this.function.apply(item);
}
finally {
ClassUtils.overrideThreadContextClassLoader(context);

View File

@@ -24,18 +24,25 @@ import org.springframework.util.ClassUtils;
* @author Dave Syer
*
*/
public class IsolatedSupplier<T> implements Supplier<T> {
public class IsolatedSupplier<T> implements Supplier<T>, Isolated {
private final Supplier<T> supplier;
private final ClassLoader classLoader;
public IsolatedSupplier(Supplier<T> supplier) {
this.supplier = supplier;
this.classLoader = supplier.getClass().getClassLoader();
}
@Override
public ClassLoader getClassLoader() {
return this.classLoader;
}
@Override
public T get() {
ClassLoader context = ClassUtils
.overrideThreadContextClassLoader(supplier.getClass().getClassLoader());
.overrideThreadContextClassLoader(this.classLoader);
try {
return supplier.get();
}