Skip to content
This repository has been archived by the owner on Mar 13, 2021. It is now read-only.

Commit

Permalink
Enhance URL parsing and fix doc (#120)
Browse files Browse the repository at this point in the history
This fixes some minor issues, replacing the `function.uri` regex with `java.net.URL` parsing. 

* Accepts  main without handler `file:/target/my.jar?main=functions.App` technically not required for `@SpringBootApplication` but does not result in error
* Accepts `handler` and `main` parameters in any order
* Protocol defaults to `file:` if not provided.
  • Loading branch information
David Turanski authored and trisberg committed Feb 1, 2019
1 parent 45fc0c4 commit 4dcd1ac
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -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<String, Object> map = new HashMap<String, Object>();
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<String, Object> 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<String, Object> map, String query) {

if (StringUtils.hasText(query)) {

Map<String, String> 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<String, Object> map) {
Map<String, Object> map) {
MapPropertySource target = null;
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -24,6 +26,7 @@

/**
* @author Dave Syer
* @author David Turanski
*
*/
public class FunctionEnvironmentPostProcessorTests {
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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();
}

}

0 comments on commit 4dcd1ac

Please sign in to comment.