diff --git a/src/main/java/io/projectriff/invoker/FunctionEnvironmentPostProcessor.java b/src/main/java/io/projectriff/invoker/FunctionEnvironmentPostProcessor.java index a81f2b4..c6918a8 100644 --- a/src/main/java/io/projectriff/invoker/FunctionEnvironmentPostProcessor.java +++ b/src/main/java/io/projectriff/invoker/FunctionEnvironmentPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-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. @@ -15,10 +15,14 @@ */ package io.projectriff.invoker; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; import java.util.HashMap; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; @@ -30,42 +34,78 @@ /** * Convert the function.uri into properties that can be consumed by the deployer library. - * - * @author Dave Syer * + * @author Dave Syer + * @author David Turanski */ public class FunctionEnvironmentPostProcessor implements EnvironmentPostProcessor { - private static Pattern uriPattern = Pattern.compile("(.+)\\?.*handler=([^&]+)&?(.*)"); - private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - String uri = environment.getProperty("function.uri"); - if (StringUtils.hasText(uri)) { - Map map = new HashMap(); - Matcher m = uriPattern.matcher(uri); - boolean matches = m.matches(); - - String jarLocation = matches ? m.group(1) : uri; - String className = matches ? m.group(2) : null; - String rest = matches ? m.group(3) : null; - if (rest != null && rest.startsWith("main=")) { - map.put("function.main", rest.substring("main=".length())); + SpringApplication application) { + + /* + * Stub to resolve non-standard protocols. + */ + URLStreamHandlerFactory urlStreamHandlerFactory = protocol -> new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) { + return null; } + }; + + URL url; - map.put("function.location", jarLocation); - if (className != null && className.trim().length()>0) { - map.put("function.bean", className); + if (environment.containsProperty("function.uri")) { + try { + /* + * Protocol defaults to file: + */ + url = new URL(new URL("file:dummy"), environment.getProperty("function.uri"), + urlStreamHandlerFactory.createURLStreamHandler("app")); } + catch (MalformedURLException e) { + throw new IllegalArgumentException(String.format("'function.uri' property %s is invalid", + environment.getProperty("function.uri"))); + } + + Map map = new HashMap<>(); + + addQueryParameters(map, url.getQuery()); + + map.put("function.location", + String.join(":", url.getProtocol(), url.getPath())); + addOrReplace(environment.getPropertySources(), map); } } + private void addQueryParameters(Map map, String query) { + + if (StringUtils.hasText(query)) { + + Map params = new HashMap<>(); + Stream.of(query.split("&")) + .forEach(s -> { + String[] pair = s.split("="); + if (pair.length == 2) { + params.put(pair[0], pair[1]); + } + }); + if (params.containsKey("main")) { + map.put("function.main", params.get("main")); + } + + if (params.containsKey("handler")) { + map.put("function.bean", params.get("handler")); + } + } + } + private void addOrReplace(MutablePropertySources propertySources, - Map map) { + Map map) { MapPropertySource target = null; if (propertySources.contains(PROPERTY_SOURCE_NAME)) { PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); diff --git a/src/test/java/io/projectriff/invoker/FunctionEnvironmentPostProcessorTests.java b/src/test/java/io/projectriff/invoker/FunctionEnvironmentPostProcessorTests.java index d4195a3..c15cf27 100644 --- a/src/test/java/io/projectriff/invoker/FunctionEnvironmentPostProcessorTests.java +++ b/src/test/java/io/projectriff/invoker/FunctionEnvironmentPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-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. @@ -15,7 +15,9 @@ */ package io.projectriff.invoker; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.core.env.StandardEnvironment; @@ -24,6 +26,7 @@ /** * @author Dave Syer + * @author David Turanski * */ public class FunctionEnvironmentPostProcessorTests { @@ -32,6 +35,9 @@ public class FunctionEnvironmentPostProcessorTests { private StandardEnvironment environment = new StandardEnvironment(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Test public void uriWithHandler() { TestPropertyValues.of("function.uri=file:target/test-classes?handler=foo") @@ -54,6 +60,18 @@ public void uriWithHandlerAndMain() { assertThat(environment.getProperty("function.main")).isEqualTo("FooFuncs"); } + @Test + public void uriWithMainOnly() { + TestPropertyValues + .of("function.uri=file:target/test-classes?main=FooFuncs") + .applyTo(environment); + processor.postProcessEnvironment(environment, null); + assertThat(environment.getProperty("function.location")) + .isEqualTo("file:target/test-classes"); + assertThat(environment.getProperty("function.bean")).isNull(); + assertThat(environment.getProperty("function.main")).isEqualTo("FooFuncs"); + } + @Test public void uriWithNoHandler() { TestPropertyValues.of("function.uri=file:target/test-classes") @@ -70,8 +88,40 @@ public void uriWithNoEmptyHandler() { .applyTo(environment); processor.postProcessEnvironment(environment, null); assertThat(environment.getProperty("function.location")) - .isEqualTo("file:target/test-classes?handler="); + .isEqualTo("file:target/test-classes"); + assertThat(environment.getProperty("function.bean")).isNull(); + } + + @Test + public void uriWithNoProtocol() { + TestPropertyValues.of("function.uri=target/test-classes") + .applyTo(environment); + processor.postProcessEnvironment(environment, null); + assertThat(environment.getProperty("function.location")) + .isEqualTo("file:target/test-classes"); + } + + @Test + public void uriWithAppProtocol() { + TestPropertyValues.of("function.uri=app:classpath?handler=io.projectriff.functions.Doubler") + .applyTo(environment); + processor.postProcessEnvironment(environment, null); + assertThat(environment.getProperty("function.location")) + .isEqualTo("app:classpath"); + assertThat(environment.getProperty("function.bean")) + .isEqualTo("io.projectriff.functions.Doubler"); + } + + @Test + public void invalidUrl() { + TestPropertyValues + .of("function.uri=file:target/test-classes&handler=foo&main=FooFuncs") + .applyTo(environment); + processor.postProcessEnvironment(environment, null); + assertThat(environment.getProperty("function.location")) + .isEqualTo("file:target/test-classes&handler=foo&main=FooFuncs"); assertThat(environment.getProperty("function.bean")).isNull(); + assertThat(environment.getProperty("function.main")).isNull(); } }