diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/InMemoryFunctionCatalog.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/InMemoryFunctionCatalog.java index bfb8a120a..b6cebdd77 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/InMemoryFunctionCatalog.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/InMemoryFunctionCatalog.java @@ -23,6 +23,7 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -65,7 +66,8 @@ public class InMemoryFunctionCatalog @Override public void register(FunctionRegistration registration) { - Assert.notEmpty(registration.getNames(), "'registration' must contain at least one name before it is registered in catalog."); + Assert.notEmpty(registration.getNames(), + "'registration' must contain at least one name before it is registered in catalog."); Class type = Object.class; if (registration.getTarget() instanceof Function) { type = Function.class; @@ -76,7 +78,8 @@ public class InMemoryFunctionCatalog else if (registration.getTarget() instanceof Consumer) { type = Consumer.class; } - FunctionRegistrationEvent event = new FunctionRegistrationEvent(this, type, registration.getNames()); + FunctionRegistrationEvent event = new FunctionRegistrationEvent(this, type, + registration.getNames()); registrations.put(registration.getTarget(), registration); FunctionRegistration wrapped = registration.wrap(); @@ -103,15 +106,17 @@ public class InMemoryFunctionCatalog public void init() { if (publisher != null && !functions.isEmpty()) { functions.keySet() - .forEach(type -> this.publishEvent(new FunctionRegistrationEvent(this, type, functions.get(type).keySet()))); + .forEach(type -> this.publishEvent(new FunctionRegistrationEvent(this, + type, functions.get(type).keySet()))); } } @PreDestroy public void close() { if (publisher != null && !functions.isEmpty()) { - functions.keySet() - .forEach(type -> this.publishEvent(new FunctionUnregistrationEvent(this, type, functions.get(type).keySet()))); + functions.keySet().forEach( + type -> this.publishEvent(new FunctionUnregistrationEvent(this, type, + functions.get(type).keySet()))); } } @@ -120,7 +125,9 @@ public class InMemoryFunctionCatalog public T lookup(Class type, String name) { T function = null; if (type == null) { - function = (T) functions.values().stream().filter(map -> map.get(name) != null).map(map -> map.get(name)).findFirst().orElse(null); + function = (T) functions.values().stream() + .filter(map -> map.get(name) != null).map(map -> map.get(name)) + .findFirst().orElse(null); } else { function = (T) this.extractTypeMap(type).get(name); @@ -130,6 +137,10 @@ public class InMemoryFunctionCatalog @Override public Set getNames(Class type) { + if (type == null) { + return functions.values().stream().flatMap(map -> map.keySet().stream()) + .collect(Collectors.toSet()); + } Map map = this.extractTypeMap(type); return map == null ? Collections.emptySet() : map.keySet(); } @@ -137,8 +148,8 @@ public class InMemoryFunctionCatalog private Map extractTypeMap(Class type) { return functions.keySet().stream() .filter(key -> key != Object.class && key.isAssignableFrom(type)) - .map(key -> functions.get(key)) - .findFirst().orElse(functions.get(Object.class)); + .map(key -> functions.get(key)).findFirst() + .orElse(functions.get(Object.class)); } private void publishEvent(Object event) { diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java index 734fbe87b..9a872c500 100644 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java +++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java @@ -35,7 +35,6 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.jar.JarFile; import java.util.jar.Manifest; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.PostConstruct; @@ -218,9 +217,11 @@ class FunctionCreatorConfiguration { Manifest manifest = getArchive().getManifest(); String mainClass = null; if (manifest != null) { - String functionClass = manifest.getMainAttributes().getValue("Function-Class"); - if (StringUtils.hasText(functionClass) && ObjectUtils.isEmpty(properties.getBean())) { - properties.setBean(new String[] {functionClass}); + String functionClass = manifest.getMainAttributes() + .getValue("Function-Class"); + if (StringUtils.hasText(functionClass) + && ObjectUtils.isEmpty(properties.getBean())) { + properties.setBean(new String[] { functionClass }); } mainClass = manifest.getMainAttributes().getValue("Start-Class"); if (mainClass == null @@ -252,7 +253,7 @@ class FunctionCreatorConfiguration { catch (MalformedURLException e) { throw new IllegalStateException("Bad URL: " + archive, e); } - }).collect(Collectors.toList()).toArray(new URL[0]); + }).toArray(URL[]::new); } private URL[] extractClasspath(String url) { @@ -597,10 +598,13 @@ class FunctionCreatorConfiguration { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - String name = FunctionProperties - .functionName(env.getProperty("function.bean", "")); - if (bean instanceof FunctionRegistry && name.contains("|")) { - bean = new SingleEntryFunctionRegistry((FunctionRegistry) bean, name); + if (bean instanceof FunctionRegistry) { + String name = FunctionProperties + .functionName(env.getProperty("function.bean", "")); + if (name.contains("|")) { + // A single composite function with an empty name + bean = new SingleEntryFunctionRegistry((FunctionRegistry) bean, name); + } } return bean; } diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistry.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistry.java index 8703e8849..81cc3ebe4 100644 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistry.java +++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistry.java @@ -20,6 +20,7 @@ import java.util.Set; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; +import org.springframework.util.StringUtils; /** * @author Dave Syer @@ -38,14 +39,18 @@ public class SingleEntryFunctionRegistry implements FunctionRegistry { @Override public T lookup(Class type, String name) { - return this.name.equals(name) ? this.delegate.lookup(type, name) : null; + if (StringUtils.isEmpty(name)) { + if (delegate.getNames(type).size() == 1) { + return delegate.lookup(type, delegate.getNames(type).iterator().next()); + } + name = this.name; + } + return name.equals(this.name) ? delegate.lookup(type, name) : null; } @Override public Set getNames(Class type) { - Set names = this.delegate.getNames(type); - return names.contains(this.name) ? Collections.singleton(this.name) - : Collections.emptySet(); + return Collections.singleton(this.name); } @Override diff --git a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistryTests.java b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistryTests.java new file mode 100644 index 000000000..679ad9712 --- /dev/null +++ b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SingleEntryFunctionRegistryTests.java @@ -0,0 +1,76 @@ +/* + * 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.deployer; + +import java.util.function.Function; + +import org.junit.Test; + +import org.springframework.cloud.function.context.FunctionRegistration; +import org.springframework.cloud.function.context.catalog.InMemoryFunctionCatalog; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Dave Syer + * + */ +public class SingleEntryFunctionRegistryTests { + + private InMemoryFunctionCatalog delegate = new InMemoryFunctionCatalog(); + + @Test + public void named() { + delegate.register(new FunctionRegistration(new Foos(), "foo")); + SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(delegate, + "foo"); + assertThat((Foos) registry.lookup("foo")).isInstanceOf(Function.class); + } + + @Test + public void other() { + delegate.register(new FunctionRegistration(new Foos(), "foo")); + SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(delegate, + "foo"); + assertThat((Foos) registry.lookup("bar")).isNull(); + } + + @Test + public void empty() { + delegate.register(new FunctionRegistration(new Foos(), "")); + SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(delegate, + ""); + assertThat((Foos) registry.lookup("")).isInstanceOf(Function.class); + } + + @Test + public void anonymous() { + delegate.register(new FunctionRegistration(new Foos(), "bar")); + SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(delegate, + "foo"); + assertThat((Foos) registry.lookup("")).isInstanceOf(Function.class); + } + + class Foos implements Function { + + @Override + public String apply(String t) { + return t.toUpperCase(); + } + + } + +}