Author: rmannibucau
Date: Sun Jun 9 15:35:30 2019
New Revision: 1860910
URL: http://svn.apache.org/viewvc?rev=1860910&view=rev
Log:
MEECROWAVE-197 some more doc on the proxy module, enable extensions
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnRequest.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnResponse.java
- copied, changed from r1860903,
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/AfterResponse.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/SpyExtension.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IOFunction.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IORunnable.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoaderTest.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/ensureDefaults.json
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/mergeWithDefaultRoute.json
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/file.txt
Modified:
openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/meecrowave-proxy/index.adoc
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/CDIProxyServlet.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/ProxyServletTest.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/mock/FakeRemoteServer.java
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/routes.json
Modified:
openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/meecrowave-proxy/index.adoc
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/meecrowave-proxy/index.adoc?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/meecrowave-proxy/index.adoc
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/meecrowave-proxy/index.adoc
Sun Jun 9 15:35:30 2019
@@ -28,6 +28,75 @@ include::../../../../../target/generated
TIP: you can use that servlet in a plain Servlet container (adding
JAX-RS+JSON-B client).
An integration example can be found in
`org.apache.meecrowave.proxy.servlet.meecrowave.ProxyServletSetup#accept`.
+== Configuration File
+
+Each route defines an execution context which means:
+
+. A way to match the incoming request (by method + prefix for now),
+. A way to forward the incoming request (which target server is called),
+. A way to execute the request isolated in a dedicated thread (how many
threads are allocated to the route, which timeout to use, ...).
+
+The routes file follows the following shape:
+
+[source,json]
+----
+{
+ "defaultRoute": { // optional
+ // ... anything a route can get, it is used as default for plain "routes"
+ },
+ "routes": [
+ {
+ "id": "get-simple",
+ "requestConfiguration": {
+ "method": "GET",
+ "prefix": "/prefix-to-match",
+ "addedHeaders" : { "Authorization": "Value", ... },
+ "skippedHeaders" : [ "Content-Length", ... ],
+ "skippedCookies" : [ "Cookie", ... ],
+ },
+ "responseConfiguration": {
+ "target": "http://....",
+ "skippedHeaders" : [ "Content-Length", ... ],
+ "skippedCookies" : [ "Cookie", ... ],
+ },
+ "clientConfiguration": {
+ "executor": {
+ "core": 8,
+ "max": 512,
+ "keepAlive": 60000,
+ "shutdownTimeout": 1
+ },
+ "timeouts": {
+ "connect": 30000,
+ "read": 30000,
+ "execution": 60000
+ },
+ "sslConfiguration": {
+ "acceptAnyCertificate": false,
+ "keystoreLocation": "...",
+ "keystoreType": "...",
+ "keystorePassword": "...",
+ "truststoreType": "...",
+ "verifiedHostnames": ["..."]
+ }
+ },
+ "extensions": { // optional, used for custom extensions and let the user
enrich the route configuration
+ }
+ },
+ // ...
+ ],
+ "extensions": { // optional
+ }
+}
+----
+
+TIP: the file is filtered with system properties so you can use
`${system-prop-key}`.
+
== Extend
-TBD
+The default implementation uses `CDIProxyServlet` which triggers multiple
events to let you extend the proxy implementation:
+
+. `BeforeRequest` and `AfterResponse` which are sent around the proxying,
+. `OnRequest` and `OnResponse` which enables you to replace the way the
request is mapped to the proxied server and the way the response of the proxied
server is mapped to the client.
+
+Since `meecrowave-proxy` is a simple meecrowave module you can embed it and
customize it as any CDI application.
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
Sun Jun 9 15:35:30 2019
@@ -19,12 +19,16 @@
package org.apache.meecrowave.proxy.servlet.configuration;
import java.util.Collection;
+import java.util.Map;
import java.util.concurrent.ExecutorService;
+import javax.enterprise.event.NotificationOptions;
+import javax.json.JsonObject;
import javax.json.bind.annotation.JsonbTransient;
import javax.ws.rs.client.Client;
public class Routes {
+ public JsonObject extensions; // placeholder for custom metadata usable in
observers
public Route defaultRoute;
public Collection<Route> routes;
@@ -38,6 +42,7 @@ public class Routes {
public RequestConfiguration requestConfiguration;
public ResponseConfiguration responseConfiguration;
public ClientConfiguration clientConfiguration;
+ public JsonObject extensions; // placeholder for custom metadata
usable in observers
@JsonbTransient
public Client client;
@@ -45,6 +50,9 @@ public class Routes {
@JsonbTransient
public ExecutorService executor;
+ @JsonbTransient
+ public NotificationOptions notificationOptions;
+
@Override
public String toString() {
return "Route{id='" + id + "', requestConfiguration=" +
requestConfiguration + ", responseConfiguration=" + responseConfiguration + '}';
@@ -52,10 +60,10 @@ public class Routes {
}
public static class ExecutorConfiguration {
- public int core = 8;
- public int max = 512;
- public long keepAlive = 60000;
- public long shutdownTimeout = 1;
+ public Integer core;
+ public Integer max;
+ public Long keepAlive;
+ public Long shutdownTimeout;
@Override
public String toString() {
@@ -69,9 +77,9 @@ public class Routes {
}
public static class TimeoutConfiguration {
- public long read = 30000;
- public long connect = 30000;
- public long execution = 60000;
+ public Long read;
+ public Long connect;
+ public Long execution;
@Override
public String toString() {
@@ -131,6 +139,7 @@ public class Routes {
public static class RequestConfiguration {
public String method;
public String prefix;
+ public Map<String, String> addedHeaders;
public Collection<String> skippedHeaders;
public Collection<String> skippedCookies;
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
Sun Jun 9 15:35:30 2019
@@ -74,13 +74,37 @@ public class ProxyServlet extends HttpSe
}
}
- protected CompletionStage<HttpServletResponse> doExecute(final
Routes.Route route, final HttpServletRequest req, final HttpServletResponse
resp,
+ protected CompletionStage<HttpServletResponse> doExecute(final
Routes.Route route,
+ final
HttpServletRequest req, final HttpServletResponse resp,
final String
prefix) throws IOException {
final AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(route.clientConfiguration.timeouts.execution);
+ return doRequest(route, req, resp, prefix).handle((response, error) ->
{
+ try {
+ if (error != null) {
+ onError(route, req, resp, error);
+ } else {
+ try {
+ forwardResponse(route, response, req, resp);
+ } catch (final IOException e) {
+ onError(route, req, resp, e);
+ }
+ }
+ } catch (final IOException ioe) {
+ getServletContext().log("Error Proxying " + req.getMethod() +
" " + req.getRequestURI() + ": " + ioe.getMessage(), ioe);
+ } finally {
+ asyncContext.complete();
+ }
+ return resp;
+ });
+ }
+
+ protected CompletionStage<Response> doRequest(final Routes.Route route,
+ final HttpServletRequest
req, final HttpServletResponse response,
+ final String prefix) throws
IOException {
WebTarget target =
route.client.target(route.responseConfiguration.target);
- target = target.path(prefix); // todo: query params, multipart, etc
+ target = target.path(prefix);
final Map<String, String> queryParams =
ofNullable(req.getQueryString())
.filter(it -> !it.isEmpty())
@@ -98,23 +122,34 @@ public class ProxyServlet extends HttpSe
target = target.queryParam(q.getKey(), q.getValue());
}
- final String type = req.getContentType();
- final Invocation.Builder request = type != null ? target.request(type)
: target.request();
+ final String type = route.requestConfiguration.addedHeaders != null ?
+
route.requestConfiguration.addedHeaders.getOrDefault("Content-Type",
req.getContentType()) :
+ req.getContentType();
+ Invocation.Builder request = type != null ? target.request(type) :
target.request();
final Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
final String name = headerNames.nextElement();
if (!filterHeader(route.requestConfiguration.skippedHeaders,
name)) {
- request.header(name, list(req.getHeaders(name)));
+ request = request.header(name, list(req.getHeaders(name)));
+ }
+ }
+
+ if (route.requestConfiguration.addedHeaders != null) {
+ for (final Map.Entry<String, String> header:
route.requestConfiguration.addedHeaders.entrySet()) {
+ request = request.header(header.getKey(), header.getValue());
}
}
final Cookie[] cookies = req.getCookies();
if (cookies != null) {
- Stream.of(cookies)
- .filter(it ->
filterCookie(route.requestConfiguration.skippedCookies, it.getName(),
it.getValue()))
- .forEach(cookie -> request.cookie(
- new javax.ws.rs.core.Cookie(cookie.getName(),
cookie.getValue(), cookie.getPath(), cookie.getDomain(), cookie.getVersion())));
+ for (final Cookie cookie : cookies) {
+ if (filterCookie(route.requestConfiguration.skippedCookies,
cookie.getName(), cookie.getValue())) {
+ continue;
+ }
+ request = request.cookie(
+ new javax.ws.rs.core.Cookie(cookie.getName(),
cookie.getValue(), cookie.getPath(), cookie.getDomain(), cookie.getVersion()));
+ }
}
final CompletionStageRxInvoker rx = request.rx();
@@ -124,35 +159,20 @@ public class ProxyServlet extends HttpSe
} else {
result = rx.method(req.getMethod());
}
- return result.handle((response, error) -> {
- try {
- if (error != null) {
- onError(route, resp, error);
- } else {
- try {
- forwardResponse(route, response, resp);
- } catch (final IOException e) {
- onError(route, resp, e);
- }
- }
- } catch (final IOException ioe) {
- getServletContext().log("Error Proxying " + req.getMethod() +
" " + req.getRequestURI() + ": " + ioe.getMessage(), ioe);
- } finally {
- asyncContext.complete();
- }
- return resp;
- });
+ return result;
}
protected boolean isWrite(final HttpServletRequest req) {
return !HttpMethod.HEAD.equalsIgnoreCase(req.getMethod()) &&
!HttpMethod.GET.equalsIgnoreCase(req.getMethod());
}
- protected void onError(final Routes.Route route, final HttpServletResponse
resp, final Throwable error) throws IOException {
+ protected void onError(final Routes.Route route,
+ final HttpServletRequest request, final
HttpServletResponse resp,
+ final Throwable error) throws IOException {
if (WebApplicationException.class.isInstance(error)) {
final WebApplicationException wae =
WebApplicationException.class.cast(error);
if (wae.getResponse() != null) {
- forwardResponse(route, wae.getResponse(), resp);
+ forwardResponse(route, wae.getResponse(), request, resp);
return;
}
}
@@ -164,7 +184,8 @@ public class ProxyServlet extends HttpSe
error.printStackTrace(new PrintWriter(resp.getOutputStream()));
}
- protected void forwardResponse(final Routes.Route route, final Response
response, final HttpServletResponse resp) throws IOException {
+ protected void forwardResponse(final Routes.Route route, final Response
response,
+ final HttpServletRequest request, final
HttpServletResponse resp) throws IOException {
final int status = response.getStatus();
resp.setStatus(status);
forwardHeaders(route, response, resp);
@@ -210,7 +231,7 @@ public class ProxyServlet extends HttpSe
}
private void writeOutput(final HttpServletResponse resp, final InputStream
stream) throws IOException {
- final int bufferSize = Math.max(1, Math.min(8192, stream.available()));
+ final int bufferSize = Math.max(128, Math.min(8192,
stream.available()));
final byte[] buffer = new byte[bufferSize]; // todo: reusable (copier?)
final ServletOutputStream outputStream = resp.getOutputStream();
int read;
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/CDIProxyServlet.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/CDIProxyServlet.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/CDIProxyServlet.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/CDIProxyServlet.java
Sun Jun 9 15:35:30 2019
@@ -19,17 +19,22 @@
package org.apache.meecrowave.proxy.servlet.front.cdi;
import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
import org.apache.meecrowave.proxy.servlet.configuration.Routes;
import org.apache.meecrowave.proxy.servlet.front.ProxyServlet;
import org.apache.meecrowave.proxy.servlet.front.cdi.event.AfterResponse;
import org.apache.meecrowave.proxy.servlet.front.cdi.event.BeforeRequest;
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.OnRequest;
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.OnResponse;
+import org.apache.meecrowave.proxy.servlet.front.cdi.extension.SpyExtension;
// IMPORTANT: don't make this class depending on meecrowave, cxf or our
internals, use setup class
public class CDIProxyServlet extends ProxyServlet {
@@ -39,17 +44,70 @@ public class CDIProxyServlet extends Pro
@Inject
private Event<AfterResponse> afterResponseEvent;
+ @Inject
+ private Event<OnRequest> onRequestEvent;
+
+ @Inject
+ private Event<OnResponse> onResponseEvent;
+
+ @Inject
+ private SpyExtension spy;
+
@Override
- protected CompletionStage<HttpServletResponse> doExecute(final
Routes.Route route, final HttpServletRequest req, final HttpServletResponse
resp,
+ protected CompletionStage<HttpServletResponse> doExecute(final
Routes.Route route,
+ final
HttpServletRequest req, final HttpServletResponse resp,
final String
prefix) throws IOException {
- final BeforeRequest event = new BeforeRequest(req, resp);
- event.setRoute(route);
- event.setPrefix(prefix);
- beforeRequestEvent.fire(event);
- return super.doExecute(event.getRoute(), req, resp, event.getPrefix())
- .handle((r, t) -> {
- afterResponseEvent.fire(new AfterResponse(req, resp));
- return r;
- });
+ final CompletionStage<HttpServletResponse> stage;
+ if (spy.isHasBeforeEvent()) {
+ final BeforeRequest event = new BeforeRequest(req, resp);
+ event.setRoute(route);
+ event.setPrefix(prefix);
+ beforeRequestEvent.fire(event);
+ stage = super.doExecute(event.getRoute(), req, resp,
event.getPrefix());
+ } else {
+ stage = super.doExecute(route, req, resp, prefix);
+ }
+ if (!spy.isHasAfterEvent()) {
+ return stage;
+ }
+ return stage.handle((r, t) -> {
+ afterResponseEvent.fire(new AfterResponse(req, resp));
+ return r;
+ });
+ }
+
+ @Override
+ protected CompletionStage<Response> doRequest(final Routes.Route route,
+ final HttpServletRequest req,
+ final HttpServletResponse
response,
+ final String prefix) throws
IOException {
+ if (spy.isHasOnRequestEvent()) {
+ final OnRequest onRequest = new OnRequest(req, response, route, r
-> super.doRequest(r, req, response, prefix));
+ return onRequestEvent.fireAsync(onRequest,
onRequest.getRoute().notificationOptions)
+ .thenCompose(it -> {
+ try {
+ return it.proceed();
+ } catch (final IOException e) {
+ final CompletableFuture<Response> future = new
CompletableFuture<>();
+ future.completeExceptionally(e);
+ return future;
+ }
+ });
+ }
+ return super.doRequest(route, req, response, prefix);
+ }
+
+ @Override
+ protected void forwardResponse(final Routes.Route route, final Response
response,
+ final HttpServletRequest request, final
HttpServletResponse resp) throws IOException {
+ if (spy.isHasOnResponseEvent()) {
+ final OnResponse onResponse = new OnResponse(request, resp,
response, () -> super.forwardResponse(route, response, request, resp));
+ onResponseEvent.fire(onResponse);
+ if (!onResponse.isProceeded()) {
+ onResponse.proceed();
+ }
+ } else {
+ super.forwardResponse(route, response, request, resp);
+ }
}
}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnRequest.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnRequest.java?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnRequest.java
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnRequest.java
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.meecrowave.proxy.servlet.front.cdi.event;
+
+import java.io.IOException;
+import java.util.concurrent.CompletionStage;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+
+import org.apache.meecrowave.proxy.servlet.configuration.Routes;
+import org.apache.meecrowave.proxy.servlet.front.cdi.func.IOFunction;
+
+public class OnRequest extends BaseEvent {
+ private Routes.Route route;
+ private IOFunction<Routes.Route, CompletionStage<Response>> delegate;
+
+ public OnRequest(final HttpServletRequest request, final
HttpServletResponse response,
+ final Routes.Route defaultRoute,
+ final IOFunction<Routes.Route, CompletionStage<Response>>
defaultImpl) {
+ super(request, response);
+ this.route = defaultRoute;
+ this.delegate = defaultImpl;
+ }
+
+ public Routes.Route getRoute() {
+ return route;
+ }
+
+ public CompletionStage<Response> proceed() throws IOException {
+ return delegate.apply(route);
+ }
+
+ public void route(final Routes.Route route) {
+ this.route = route;
+ }
+
+ public void provide(final IOFunction<Routes.Route,
CompletionStage<Response>> impl) {
+ this.delegate = impl;
+ }
+
+ public void provide(final CompletionStage<Response> promise) {
+ provide(route -> promise);
+ }
+}
Copied:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnResponse.java
(from r1860903,
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/AfterResponse.java)
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnResponse.java?p2=openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnResponse.java&p1=openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/AfterResponse.java&r1=1860903&r2=1860910&rev=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/AfterResponse.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/event/OnResponse.java
Sun Jun 9 15:35:30 2019
@@ -18,32 +18,36 @@
*/
package org.apache.meecrowave.proxy.servlet.front.cdi.event;
+import java.io.IOException;
+
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
-import org.apache.meecrowave.proxy.servlet.configuration.Routes;
+import org.apache.meecrowave.proxy.servlet.front.cdi.func.IORunnable;
-public class AfterResponse extends BaseEvent {
- private Routes.Route route;
- private String prefix;
+public class OnResponse extends BaseEvent {
+ private final Response clientResponse;
+ private final IORunnable delegate;
+ private boolean proceeded;
- public AfterResponse(final HttpServletRequest request, final
HttpServletResponse response) {
+ public OnResponse(final HttpServletRequest request, final
HttpServletResponse response,
+ final Response clientResponse, final IORunnable
delegate) {
super(request, response);
+ this.clientResponse = clientResponse;
+ this.delegate = delegate;
}
- public String getPrefix() {
- return prefix;
- }
-
- public void setPrefix(final String prefix) {
- this.prefix = prefix;
+ public Response getClientResponse() {
+ return clientResponse;
}
- public Routes.Route getRoute() {
- return route;
+ public void proceed() throws IOException {
+ delegate.run();
+ proceeded = true;
}
- public void setRoute(final Routes.Route route) {
- this.route = route;
+ public boolean isProceeded() {
+ return proceeded;
}
}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/SpyExtension.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/SpyExtension.java?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/SpyExtension.java
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/extension/SpyExtension.java
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.meecrowave.proxy.servlet.front.cdi.extension;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessObserverMethod;
+
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.AfterResponse;
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.BeforeRequest;
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.OnRequest;
+import org.apache.meecrowave.proxy.servlet.front.cdi.event.OnResponse;
+
+public class SpyExtension implements Extension {
+ private boolean hasBeforeEvent;
+ private boolean hasAfterEvent;
+ private boolean hasOnRequestEvent;
+ private boolean hasOnResponseEvent;
+
+ void onBeforeObserver(@Observes final ProcessObserverMethod<BeforeRequest,
?> processObserverMethod) {
+ hasBeforeEvent = true;
+ }
+
+ void onAfterObserver(@Observes final ProcessObserverMethod<AfterResponse,
?> processObserverMethod) {
+ hasAfterEvent = true;
+ }
+
+ void onRequestObserver(@Observes final ProcessObserverMethod<OnRequest, ?>
processObserverMethod) {
+ hasOnRequestEvent = true;
+ }
+
+ void onResponseObserver(@Observes final ProcessObserverMethod<OnResponse,
?> processObserverMethod) {
+ hasOnResponseEvent = true;
+ }
+
+ public boolean isHasOnRequestEvent() {
+ return hasOnRequestEvent;
+ }
+
+ public boolean isHasOnResponseEvent() {
+ return hasOnResponseEvent;
+ }
+
+ public boolean isHasBeforeEvent() {
+ return hasBeforeEvent;
+ }
+
+ public boolean isHasAfterEvent() {
+ return hasAfterEvent;
+ }
+}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IOFunction.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IOFunction.java?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IOFunction.java
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IOFunction.java
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.meecrowave.proxy.servlet.front.cdi.func;
+
+import java.io.IOException;
+
+@FunctionalInterface
+public interface IOFunction<A, T> {
+ T apply(A a) throws IOException;
+}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IORunnable.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IORunnable.java?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IORunnable.java
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/cdi/func/IORunnable.java
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.meecrowave.proxy.servlet.front.cdi.func;
+
+import java.io.IOException;
+
+@FunctionalInterface
+public interface IORunnable {
+ void run() throws IOException;
+}
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
Sun Jun 9 15:35:30 2019
@@ -18,10 +18,12 @@
*/
package org.apache.meecrowave.proxy.servlet.service;
+import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import java.io.ByteArrayOutputStream;
@@ -41,11 +43,19 @@ import java.security.NoSuchAlgorithmExce
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Optional;
-import java.util.concurrent.ExecutorService;
+import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+import javax.enterprise.event.NotificationOptions;
+import javax.json.Json;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
@@ -54,6 +64,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import org.apache.meecrowave.proxy.servlet.configuration.Routes;
@@ -70,6 +81,10 @@ public abstract class ConfigurationLoade
protected abstract void log(String message);
public Optional<Routes> load() {
+ return doLoad(readContent());
+ }
+
+ protected String readContent() {
final SimpleSubstitutor simpleSubstitutor = new SimpleSubstitutor(
System.getProperties().stringPropertyNames().stream().collect(toMap(identity(),
System::getProperty)));
final String resource = simpleSubstitutor.replace(path);
@@ -87,38 +102,80 @@ public abstract class ConfigurationLoade
if (stream == null) {
throw new IllegalArgumentException("No routes configuration for
the proxy servlet");
}
-
- final String content;
try {
- content = simpleSubstitutor.replace(load(stream));
+ return simpleSubstitutor.replace(load(stream));
} catch (final IOException e) {
throw new IllegalArgumentException(e);
}
+ }
+ protected Optional<Routes> doLoad(final String content) {
try (final Jsonb jsonb = JsonbBuilder.newBuilder()
.withProvider(loadJsonpProvider())
.withConfig(new
JsonbConfig().setProperty("org.apache.johnzon.supports-comments", true))
.build()) {
routes = jsonb.fromJson(content, Routes.class);
- } catch (final Exception e) {
- throw new IllegalArgumentException(e);
- }
- final boolean hasRoutes = routes.routes != null &&
!routes.routes.isEmpty();
- if (routes.defaultRoute == null && !hasRoutes) {
- return Optional.empty();
- }
- if (routes.defaultRoute != null) {
- if (routes.routes == null) { // no route were defined, consider it
is the default route, /!\ empty means no route, don't default
- routes.routes = singletonList(routes.defaultRoute);
+ final boolean hasRoutes = routes.routes != null &&
!routes.routes.isEmpty();
+ if (routes.defaultRoute == null && !hasRoutes) {
+ return Optional.empty();
}
if (hasRoutes) {
- routes.routes.forEach(r -> merge(routes.defaultRoute, r));
+ // before merging, ensure all routes have an id to not
duplicate default id
+ routes.routes.stream().filter(it -> it.id == null).forEach(it
-> it.id = newId());
}
+ if (routes.defaultRoute != null) {
+ if (routes.defaultRoute.id == null) {
+ routes.defaultRoute.id = "default";
+ }
+ if (routes.routes == null) { // no route were defined,
consider it is the default route, /!\ empty means no route, don't default
+ routes.routes = singletonList(routes.defaultRoute);
+ }
+ if (hasRoutes) {
+ final JsonBuilderFactory jsonFactory =
Json.createBuilderFactory(emptyMap());
+ final JsonObject template =
jsonb.fromJson(jsonb.toJson(routes.defaultRoute), JsonObject.class);
+ routes.routes = routes.routes.stream().map(r ->
merge(jsonb, jsonFactory, template, r)).collect(toList());
+ }
+ }
+ } catch (final Exception e) {
+ throw new IllegalArgumentException(e);
}
- routes.routes.forEach(this::loadClient);
+ routes.routes.forEach(this::init);
return Optional.of(routes);
}
+ protected Routes.Route merge(final Jsonb jsonb, final JsonBuilderFactory
jsonFactory,
+ final JsonObject template, final Routes.Route
current) {
+ final JsonObject merged = doMerge(jsonFactory, template,
jsonb.fromJson(jsonb.toJson(current), JsonObject.class));
+ return jsonb.fromJson(merged.toString(), Routes.Route.class);
+ }
+
+ private JsonObject doMerge(final JsonBuilderFactory jsonFactory, final
JsonObject template, final JsonObject currentRoute) {
+ if (currentRoute == null) {
+ return template;
+ }
+ if (template == null) {
+ return currentRoute;
+ }
+ return Stream.concat(template.keySet().stream(),
currentRoute.keySet().stream())
+ .distinct()
+ .collect(Collector.of(jsonFactory::createObjectBuilder, (builder,
key) -> {
+ final JsonValue templateValue = template.get(key);
+ final JsonValue value =
ofNullable(currentRoute.get(key)).orElse(templateValue);
+ switch (value.getValueType()) {
+ case NULL:
+ break;
+ case OBJECT:
+ builder.add(key, templateValue != null ?
+ doMerge(jsonFactory,
templateValue.asJsonObject(), value.asJsonObject()) :
+ value);
+ break;
+ default: // primitives + array, get or replace logic since
it is "values"
+ builder.add(key, value);
+ break;
+ }
+ }, JsonObjectBuilder::addAll, JsonObjectBuilder::build));
+ }
+
private String load(final InputStream stream) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
@@ -129,43 +186,16 @@ public abstract class ConfigurationLoade
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
}
- private void loadClient(final Routes.Route route) {
- if (route.clientConfiguration == null) {
- route.clientConfiguration = new Routes.ClientConfiguration();
- }
- if (route.clientConfiguration.executor == null) {
- route.clientConfiguration.executor = new
Routes.ExecutorConfiguration();
- }
- if (route.clientConfiguration.timeouts == null) {
- route.clientConfiguration.timeouts = new
Routes.TimeoutConfiguration();
- }
- if (route.clientConfiguration.sslConfiguration == null) {
- route.clientConfiguration.sslConfiguration = new
Routes.SslConfiguration();
- }
-
- final ExecutorService executor = new ThreadPoolExecutor(
- route.clientConfiguration.executor.core,
- route.clientConfiguration.executor.max,
- route.clientConfiguration.executor.keepAlive,
- MILLISECONDS,
- new LinkedBlockingQueue<>(),
- new ThreadFactory() {
- private final SecurityManager sm =
System.getSecurityManager();
- private final ThreadGroup group = (sm != null) ?
sm.getThreadGroup() : Thread.currentThread().getThreadGroup();
-
- @Override
- public Thread newThread(final Runnable r) {
- final Thread newThread = new Thread(group, r,
"meecrowave-proxy#" + ofNullable(route.id).orElse("[noid]"));
- newThread.setDaemon(false);
- newThread.setPriority(Thread.NORM_PRIORITY);
-
newThread.setContextClassLoader(getClass().getClassLoader());
- return newThread;
- }
- },
- (run, ex) -> log("Proxy rejected task: " + run + ", in " +
ex));
+ private void init(final Routes.Route route) {
+ enforceClientConfiguration(route);
+ route.executor = createExecutor(route);
+ route.notificationOptions =
NotificationOptions.ofExecutor(route.executor);
+ route.client = createClient(route);
+ }
+ private Client createClient(final Routes.Route route) {
final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
- clientBuilder.executorService(executor);
+ clientBuilder.executorService(route.executor);
clientBuilder.readTimeout(route.clientConfiguration.timeouts.read,
MILLISECONDS);
clientBuilder.connectTimeout(route.clientConfiguration.timeouts.connect,
MILLISECONDS);
// clientBuilder.scheduledExecutorService(); // not used by cxf for
instance so no need to overkill the conf
@@ -184,72 +214,80 @@ public abstract class ConfigurationLoade
route.clientConfiguration.sslConfiguration.truststoreType));
}
- route.client = clientBuilder.build();
+ return clientBuilder.build();
}
- private void merge(final Routes.Route defaultRoute, final Routes.Route
route) {
- // request matching
- if (route.requestConfiguration == null) {
- route.requestConfiguration = defaultRoute.requestConfiguration;
- } else if (defaultRoute.requestConfiguration != null) {
- if (route.requestConfiguration.method == null) {
- route.requestConfiguration.method =
defaultRoute.requestConfiguration.method;
- }
- if (route.requestConfiguration.prefix == null) {
- route.requestConfiguration.prefix =
defaultRoute.requestConfiguration.prefix;
- }
- if (route.requestConfiguration.skippedCookies == null) {
- route.requestConfiguration.skippedCookies =
defaultRoute.requestConfiguration.skippedCookies;
- }
- if (route.requestConfiguration.skippedHeaders == null) {
- route.requestConfiguration.skippedHeaders =
defaultRoute.requestConfiguration.skippedHeaders;
- }
+ protected String newId() {
+ return UUID.randomUUID().toString();
+ }
+
+ private ThreadPoolExecutor createExecutor(final Routes.Route route) {
+ return new ThreadPoolExecutor(
+ route.clientConfiguration.executor.core,
+ Math.max(route.clientConfiguration.executor.max,
route.clientConfiguration.executor.core),
+ route.clientConfiguration.executor.keepAlive,
+ MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ new ThreadFactory() {
+ private final SecurityManager sm = System.getSecurityManager();
+ private final ThreadGroup group = (sm != null) ?
sm.getThreadGroup() : Thread.currentThread().getThreadGroup();
+
+ @Override
+ public Thread newThread(final Runnable r) {
+ final Thread newThread = new Thread(group, r,
"meecrowave-proxy#" + route.id);
+ newThread.setDaemon(false);
+ newThread.setPriority(Thread.NORM_PRIORITY);
+
newThread.setContextClassLoader(getClass().getClassLoader());
+ return newThread;
+ }
+ },
+ (run, ex) -> log("Proxy rejected task: " + run + ", in " + ex));
+ }
+
+ private void enforceClientConfiguration(final Routes.Route route) {
+ if (route.clientConfiguration == null) {
+ route.clientConfiguration = new Routes.ClientConfiguration();
}
- // response processing
- if (route.responseConfiguration == null) {
- route.responseConfiguration = defaultRoute.responseConfiguration;
- } else if (defaultRoute.responseConfiguration != null) {
- if (route.responseConfiguration.target == null) {
- route.responseConfiguration.target =
defaultRoute.responseConfiguration.target;
- }
- if (route.responseConfiguration.skippedCookies == null) {
- route.responseConfiguration.skippedCookies =
defaultRoute.responseConfiguration.skippedCookies;
- }
- if (route.responseConfiguration.skippedHeaders == null) {
- route.responseConfiguration.skippedHeaders =
defaultRoute.responseConfiguration.skippedHeaders;
- }
+ if (route.clientConfiguration.executor == null) {
+ route.clientConfiguration.executor = new
Routes.ExecutorConfiguration();
}
+ ensureExecutorDefaults(route.clientConfiguration.executor);
- // client setup
- if (route.clientConfiguration == null) {
- route.clientConfiguration = defaultRoute.clientConfiguration;
- } else if (defaultRoute.clientConfiguration != null) {
- if (route.clientConfiguration.sslConfiguration == null) {
- route.clientConfiguration.sslConfiguration =
defaultRoute.clientConfiguration.sslConfiguration;
- } else if (defaultRoute.clientConfiguration.sslConfiguration !=
null) {
- if
(route.clientConfiguration.sslConfiguration.verifiedHostnames == null) {
-
route.clientConfiguration.sslConfiguration.verifiedHostnames =
defaultRoute.clientConfiguration.sslConfiguration.verifiedHostnames;
- }
- if
(route.clientConfiguration.sslConfiguration.keystoreLocation == null) {
-
route.clientConfiguration.sslConfiguration.keystoreLocation =
defaultRoute.clientConfiguration.sslConfiguration.keystoreLocation;
- }
- if
(route.clientConfiguration.sslConfiguration.keystorePassword == null) {
-
route.clientConfiguration.sslConfiguration.keystorePassword =
defaultRoute.clientConfiguration.sslConfiguration.keystorePassword;
- }
- if (route.clientConfiguration.sslConfiguration.keystoreType ==
null) {
- route.clientConfiguration.sslConfiguration.keystoreType =
defaultRoute.clientConfiguration.sslConfiguration.keystoreType;
- }
- if (route.clientConfiguration.sslConfiguration.truststoreType
== null) {
- route.clientConfiguration.sslConfiguration.truststoreType
= defaultRoute.clientConfiguration.sslConfiguration.truststoreType;
- }
- }
- if (route.clientConfiguration.executor == null) {
- route.clientConfiguration.executor =
defaultRoute.clientConfiguration.executor;
- }
- if (route.clientConfiguration.timeouts == null) {
- route.clientConfiguration.timeouts =
defaultRoute.clientConfiguration.timeouts;
- }
+ if (route.clientConfiguration.timeouts == null) {
+ route.clientConfiguration.timeouts = new
Routes.TimeoutConfiguration();
+ }
+ ensureTimeoutsDefaults(route.clientConfiguration.timeouts);
+
+ if (route.clientConfiguration.sslConfiguration == null) {
+ route.clientConfiguration.sslConfiguration = new
Routes.SslConfiguration();
+ }
+ }
+
+ private void ensureTimeoutsDefaults(final Routes.TimeoutConfiguration
timeouts) {
+ if (timeouts.read == null) {
+ timeouts.read = 30000L;
+ }
+ if (timeouts.connect == null) {
+ timeouts.connect = 30000L;
+ }
+ if (timeouts.execution == null) {
+ timeouts.execution = 60000L;
+ }
+ }
+
+ private void ensureExecutorDefaults(final Routes.ExecutorConfiguration
executor) {
+ if (executor.core == null) {
+ executor.core =
Math.max(Runtime.getRuntime().availableProcessors() * 2, 2);
+ }
+ if (executor.max == null) {
+ executor.max = 512;
+ }
+ if (executor.keepAlive == null) {
+ executor.keepAlive = 60000L;
+ }
+ if (executor.shutdownTimeout == null) {
+ executor.shutdownTimeout = 1L;
}
}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
Sun Jun 9 15:35:30 2019
@@ -0,0 +1 @@
+org.apache.meecrowave.proxy.servlet.front.cdi.extension.SpyExtension
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/ProxyServletTest.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/ProxyServletTest.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/ProxyServletTest.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/ProxyServletTest.java
Sun Jun 9 15:35:30 2019
@@ -18,13 +18,16 @@
*/
package org.apache.meecrowave.proxy.servlet;
+import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
import static javax.ws.rs.client.Entity.entity;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.io.IOException;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
@@ -34,6 +37,9 @@ import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.apache.meecrowave.Meecrowave;
import org.apache.meecrowave.io.IO;
import org.apache.meecrowave.junit.MeecrowaveRule;
@@ -45,7 +51,7 @@ import org.junit.rules.TestRule;
public class ProxyServletTest {
@ClassRule(order = 1)
public static final TestRule FAKE_REMOTE_SERVER = new FakeRemoteServer()
- .with(server -> server.createContext("/simple", exchange -> {
+ .with((server, helper) -> server.createContext("/simple", exchange
-> {
final byte[] out = ("{\"message\":\"" +
ofNullable(exchange.getRequestBody()).map(it -> {
try {
return IO.toString(it);
@@ -55,19 +61,18 @@ public class ProxyServletTest {
}).orElse("ok") + "\"}").getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().add("Fake-Server", "true");
exchange.getResponseHeaders().add("Foo",
ofNullable(exchange.getRequestURI().getQuery()).orElse("-"));
- exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK,
out.length);
- try (final OutputStream os = exchange.getResponseBody()) {
- os.write(out);
- }
+ helper.response(exchange, HttpURLConnection.HTTP_OK, out);
}))
- .with(server -> server.createContext("/data1", exchange -> {
+ .with((server, helper) -> server.createContext("/data1", exchange
-> {
final byte[] out = ("{\"message\":\"" +
IO.toString(exchange.getRequestBody()) + "\"}")
.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().add("Fake-Server", "posted");
- exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK,
out.length);
- try (final OutputStream os = exchange.getResponseBody()) {
- os.write(out);
- }
+ helper.response(exchange, HttpURLConnection.HTTP_OK, out);
+ }))
+ .with((server, helper) -> server.createContext("/upload1",
exchange -> {
+ final byte[] out = ("{\"message\":\"" +
IO.toString(exchange.getRequestBody()) + "\"}")
+ .getBytes(StandardCharsets.UTF_8);
+ helper.response(exchange, HttpURLConnection.HTTP_OK, out);
}));
@ClassRule(order = 2)
@@ -75,6 +80,27 @@ public class ProxyServletTest {
.property("proxy-configuration",
"target/test-classes/routes.json"), "");
@Test
+ public void upload() {
+ withClient(target -> {
+ final Response response = target.path("/upload1").request()
+ .post(entity(new MultipartBody(asList(
+ new Attachment("metadata", APPLICATION_JSON,
"{\"content\":\"text\"}"),
+ new Attachment(
+ "file",
+
Thread.currentThread().getContextClassLoader().getResourceAsStream("ProxyServletTest/upload/file.txt"),
+ new ContentDisposition("uploadded.txt"))
+ )), MULTIPART_FORM_DATA));
+ assertEquals(HttpURLConnection.HTTP_OK, response.getStatus());
+ final String actual = response.readEntity(String.class);
+ assertTrue(actual, actual.contains("uuid:"));
+ assertTrue(actual, actual.contains("Content-Type:
application/json"));
+ assertTrue(actual, actual.contains("{\"content\":\"text\"}"));
+ assertTrue(actual, actual.contains("Content-Type:
application/octet-stream"));
+ assertTrue(actual,
actual.contains("test\nfile\nwith\nmultiple\nlines"));
+ });
+ }
+
+ @Test
public void get() {
withClient(target -> {
final Response response = target.path("/simple").request().get();
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/mock/FakeRemoteServer.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/mock/FakeRemoteServer.java?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/mock/FakeRemoteServer.java
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/mock/FakeRemoteServer.java
Sun Jun 9 15:35:30 2019
@@ -18,25 +18,30 @@
*/
package org.apache.meecrowave.proxy.servlet.mock;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
+import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class FakeRemoteServer implements TestRule {
private HttpServer server;
- private final Collection<Consumer<HttpServer>> configurers = new
ArrayList<>();
+ private final Collection<BiConsumer<HttpServer, HttpHelper>> configurers =
new ArrayList<BiConsumer<HttpServer, HttpHelper>>();
public HttpServer getServer() {
return server;
}
- public FakeRemoteServer with(final Consumer<HttpServer> configurer) {
+ public FakeRemoteServer with(final BiConsumer<HttpServer, HttpHelper>
configurer) {
configurers.add(configurer);
return this;
}
@@ -48,7 +53,12 @@ public class FakeRemoteServer implements
public void evaluate() throws Throwable {
try {
server = HttpServer.create(new InetSocketAddress(0), 0);
- configurers.forEach(it -> it.accept(server));
+ configurers.forEach(it -> it.accept(server, (exchange,
status, payload) -> {
+
exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, payload.length);
+ try (final OutputStream os =
exchange.getResponseBody()) {
+ os.write(payload);
+ }
+ }));
server.start();
System.setProperty("fake.server.port",
Integer.toString(server.getAddress().getPort()));
statement.evaluate();
@@ -60,4 +70,8 @@ public class FakeRemoteServer implements
}
};
}
+
+ public interface HttpHelper {
+ void response(HttpExchange exchange, int status, byte[] payload)
throws IOException ;
+ }
}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoaderTest.java
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoaderTest.java?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoaderTest.java
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoaderTest.java
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.meecrowave.proxy.servlet.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Optional;
+
+import org.apache.meecrowave.proxy.servlet.configuration.Routes;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+public class ConfigurationLoaderTest {
+ @Rule
+ public final TestName testName = new TestName();
+
+ @Test
+ public void mergeWithDefaultRoute() {
+ doLoad().map(routes -> {
+ final Routes.Route route = routes.routes.iterator().next();
+ assertEquals(route.clientConfiguration.executor.core.intValue(),
1);
+ assertEquals(route.clientConfiguration.timeouts.read.longValue(),
6789);
+
assertEquals(route.clientConfiguration.timeouts.connect.longValue(), 1235);
+ return routes;
+ }).orElseThrow(AssertionError::new);
+ }
+
+ @Test
+ public void ensureDefaults() {
+ doLoad().map(routes -> {
+ final Routes.Route route = routes.routes.iterator().next();
+ assertEquals(route.clientConfiguration.executor.core.intValue(),
8);
+ assertEquals(route.clientConfiguration.timeouts.read.longValue(),
30000);
+
assertEquals(route.clientConfiguration.timeouts.connect.longValue(), 30000);
+ return routes;
+ }).orElseThrow(AssertionError::new);
+ }
+
+ private Optional<Routes> doLoad() {
+ return new ConfigurationLoader(getClass().getSimpleName() + '/' +
testName.getMethodName() + ".json") {
+ @Override
+ protected void log(final String message) {
+ // no-op
+ }
+ }.load();
+ }
+}
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/ensureDefaults.json
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/ensureDefaults.json?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/ensureDefaults.json
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/ensureDefaults.json
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,11 @@
+{
+ "routes": [
+ {
+ "id": "get-simple",
+ "requestConfiguration": {
+ "method": "GET",
+ "prefix": "/simple"
+ }
+ }
+ ]
+}
\ No newline at end of file
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/mergeWithDefaultRoute.json
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/mergeWithDefaultRoute.json?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/mergeWithDefaultRoute.json
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ConfigurationLoaderTest/mergeWithDefaultRoute.json
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,30 @@
+{
+ "defaultRoute": {
+ "responseConfiguration": {
+ "target": "http://localhost:${fake.server.port}"
+ },
+ "clientConfiguration": {
+ "timeouts": {
+ "read": 1234,
+ "connect": 1235
+ },
+ "executor": {
+ "core": 1
+ }
+ }
+ },
+ "routes": [
+ {
+ "id": "get-simple",
+ "requestConfiguration": {
+ "method": "GET",
+ "prefix": "/simple"
+ },
+ "clientConfiguration": {
+ "timeouts": {
+ "read": 6789
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
Added:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/file.txt
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/file.txt?rev=1860910&view=auto
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/file.txt
(added)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/ProxyServletTest/upload/file.txt
Sun Jun 9 15:35:30 2019
@@ -0,0 +1,5 @@
+test
+file
+with
+multiple
+lines
Modified:
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/routes.json
URL:
http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/routes.json?rev=1860910&r1=1860909&r2=1860910&view=diff
==============================================================================
---
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/routes.json
(original)
+++
openwebbeans/meecrowave/trunk/meecrowave-proxy/src/test/resources/routes.json
Sun Jun 9 15:35:30 2019
@@ -23,6 +23,13 @@
"method": "POST",
"prefix": "/data1"
}
+ },
+ {
+ "id": "multipart",
+ "requestConfiguration": {
+ "method": "POST",
+ "prefix": "/upload1"
+ }
}
]
}
\ No newline at end of file