Skip to content

Commit

Permalink
- Add uri templating/parameters to client requests
Browse files Browse the repository at this point in the history
- Fix for entities/chunked streaming in responses
  • Loading branch information
rchodava committed Nov 8, 2015
1 parent 63cb69b commit 2c097d9
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 11 deletions.
27 changes: 20 additions & 7 deletions core/src/main/java/org/chodavarapu/datamill/http/Client.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.chodavarapu.datamill.http;

import com.google.common.base.Joiner;
import org.chodavarapu.datamill.http.impl.InputStreamEntity;
import org.chodavarapu.datamill.http.impl.RequestBuilderImpl;
import org.chodavarapu.datamill.http.impl.ResponseImpl;
import org.chodavarapu.datamill.http.impl.ValueEntity;
import org.chodavarapu.datamill.http.impl.*;
import org.chodavarapu.datamill.values.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -28,10 +25,11 @@
*/
public class Client {
private static final Logger logger = LoggerFactory.getLogger(Client.class);
private final TemplateBasedUriBuilder uriBuilder = new TemplateBasedUriBuilder();

public Observable<Response> request(Function<RequestBuilder, Request> builder) {
Request request = builder.apply(new RequestBuilderImpl());
return request(request.method(), request.headers(), request.uri(), request.entity());
return request(request.method(), request.headers(), request.uri(), request.uriParameters(), request.entity());
}

public Observable<Response> request(Method method, Map<String, String> headers, String uri, Value entity) {
Expand All @@ -43,8 +41,23 @@ protected URLConnection createConnection(String uri) throws IOException {
}

public Observable<Response> request(Method method, Map<String, String> headers, String uri, Entity entity) {
return request(method, headers, uri, null, entity);
}

public Observable<Response> request(
Method method,
Map<String, String> headers,
String uri,
Map<String, String> uriParameters,
Entity entity) {
if (uriParameters != null && uriParameters.size() > 0) {
uri = uriBuilder.build(uri, uriParameters);
}

final String composedUri = uri;

return Async.fromCallable(() -> {
URLConnection urlConnection = createConnection(uri);
URLConnection urlConnection = createConnection(composedUri);
HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;

httpConnection.setRequestMethod(method.toString());
Expand All @@ -59,7 +72,7 @@ public Observable<Response> request(Method method, Map<String, String> headers,
writeEntityOutOverConnection(entity, httpConnection);
}

logger.debug("Making HTTP request {} {}", method.name(), uri);
logger.debug("Making HTTP request {} {}", method.name(), composedUri);
if (headers != null && logger.isDebugEnabled()) {
logger.debug(" HTTP request headers:");
for (Map.Entry<String, String> header : headers.entrySet()) {
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/chodavarapu/datamill/http/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public interface Request {
String uri();

Value uriParameter(String parameter);

Map<String, String> uriParameters();
}
14 changes: 13 additions & 1 deletion core/src/main/java/org/chodavarapu/datamill/http/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServer;
import org.chodavarapu.datamill.http.builder.RouteBuilder;
import org.chodavarapu.datamill.http.impl.ServerRequestImpl;
Expand Down Expand Up @@ -34,7 +35,18 @@ public void start(Future<Void> startFuture) throws Exception {
responseObservable.doOnNext(routeResponse -> {
if (routeResponse != null) {
r.response().setStatusCode(routeResponse.status().getCode());
r.response().end(routeResponse.entity() == null ? "" : routeResponse.entity().toString());

if (routeResponse.entity() == null) {
r.response().end();
} else {
r.response().setChunked(true);
routeResponse.entity().asChunks()
.doOnNext(bytes -> r.response().write(Buffer.buffer(bytes)))
.doOnError(throwable -> r.response().end())
.doOnCompleted(() -> r.response().end())
.subscribe();
}

return;
}
}).subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public class RequestBuilderImpl implements RequestBuilder {
private String method;
private final Map<String, String> headers = new HashMap<>();
private String uri;
private final Map<String, String> uriParameters = new HashMap<>();

@Override
public Request build() {
return new RequestImpl(method, headers, uri, entity);
return new RequestImpl(method, headers, uri, uriParameters, entity);
}

@Override
Expand Down Expand Up @@ -63,6 +64,7 @@ public RequestBuilder uri(String uri) {

@Override
public <T> RequestBuilder uriParameter(String name, T value) {
this.uriParameters.put(name, value.toString());
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ public class RequestImpl implements Request {
private final String method;
private final Map<String, String> headers;
private final String uri;
private final Map<String, String> uriParameters;

public RequestImpl(String method, Map<String, String> headers, String uri, Entity entity) {
public RequestImpl(String method, Map<String, String> headers, String uri, Map<String, String> uriParameters, Entity entity) {
this.method = method;
this.uri = uri;
this.uriParameters = uriParameters;
this.headers = headers;
this.entity = entity;
}
Expand Down Expand Up @@ -59,6 +61,16 @@ public String uri() {

@Override
public Value uriParameter(String parameter) {
return null;
String value = uriParameters.get(parameter);
if (value == null) {
return null;
}

return new StringValue(value);
}

@Override
public Map<String, String> uriParameters() {
return uriParameters;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ public Value uriParameter(String parameter) {
}
return null;
}

@Override
public Map<String, String> uriParameters() {
return uriParameters;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.chodavarapu.datamill.http.impl;

import java.util.Map;
import java.util.regex.Pattern;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class TemplateBasedUriBuilder {
private static final Pattern placeholderPattern = Pattern.compile("\\{([a-z]+)}");

public String build(String uri, Map<String, String> uriParameters) {
StringBuilder composedUri = new StringBuilder();

java.util.regex.Matcher matcher = placeholderPattern.matcher(uri);
int searchIx = 0;
while (matcher.find(searchIx)) {
composedUri.append(uri.substring(searchIx, matcher.start()));

String parameterName = matcher.group(1);
if (parameterName != null && parameterName.length() > 1) {
String value = uriParameters.get(parameterName);
if (value != null) {
composedUri.append(value);
} else {
composedUri.append(matcher.group());
}
}

searchIx = matcher.end();
}

if (searchIx < uri.length()) {
composedUri.append(uri.substring(searchIx));
}

return composedUri.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.chodavarapu.datamill.http.impl;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import static org.junit.Assert.assertEquals;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class InputStreamEntityTest {
@Test
public void entityStreaming() {
assertEquals("test", new InputStreamEntity(new ByteArrayInputStream("test".getBytes()))
.asString().toBlocking().last());
assertEquals("test", new String(new InputStreamEntity(new ByteArrayInputStream("test".getBytes()))
.asBytes().toBlocking().last()));
assertEquals("test", new String(new InputStreamEntity(new ByteArrayInputStream("test".getBytes()))
.asChunks()
.collect(
() -> new ByteArrayOutputStream(),
(stream, chunk) -> {
try {
stream.write(chunk);
} catch (IOException e) {
}
})
.map(stream -> stream.toByteArray()).toBlocking().last()));
assertEquals("value", new InputStreamEntity(new ByteArrayInputStream("{\"name\":\"value\"}".getBytes()))
.asJson().toBlocking().last().get("name").asString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.chodavarapu.datamill.http.impl;

import com.google.common.collect.ImmutableMap;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class TemplateBasedUriBuilderTest {
@Test
public void templateBasedUriBuilding() {
TemplateBasedUriBuilder builder = new TemplateBasedUriBuilder();

assertEquals("http://localhost:9080/users/0/repositories",
builder.build("http://{host}:{port}/users/{id}/repositories",
ImmutableMap.of("host", "localhost", "port", "9080", "id", "0")));
assertEquals("https://localhost:9080/users/0/repositories",
builder.build("{scheme}://{host}:{port}/users/{id}/repositories",
ImmutableMap.of("host", "localhost", "port", "9080", "id", "0", "scheme", "https")));
assertEquals("http://localhost:9080/users/0/repositories",
builder.build("http://{host}:{port}/users/{id}/{association}",
ImmutableMap.of("host", "localhost", "port", "9080", "id", "0", "association", "repositories")));
assertEquals("http://localhost:9080/users/0/repositories/{unmatched}",
builder.build("http://{host}:{port}/users/{id}/{association}/{unmatched}",
ImmutableMap.of("host", "localhost", "port", "9080", "id", "0", "association", "repositories")));
assertEquals("http://localhost:9080/users",
builder.build("{scheme}{host}:{port}{path}",
ImmutableMap.of("scheme", "http://", "host", "localhost", "port", "9080", "path", "/users")));
}
}

0 comments on commit 2c097d9

Please sign in to comment.