
io.fluxcapacitor.javaclient.web.ForwardingWebConsumer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client Show documentation
Show all versions of java-client Show documentation
Default Java client library for interfacing with Flux Capacitor.
/*
* Copyright (c) Flux Capacitor IP B.V. or its affiliates. All Rights Reserved.
*
* 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 io.fluxcapacitor.javaclient.web;
import io.fluxcapacitor.common.Guarantee;
import io.fluxcapacitor.common.MessageType;
import io.fluxcapacitor.common.Registration;
import io.fluxcapacitor.common.api.Data;
import io.fluxcapacitor.common.api.Metadata;
import io.fluxcapacitor.common.api.SerializedMessage;
import io.fluxcapacitor.javaclient.FluxCapacitor;
import io.fluxcapacitor.javaclient.common.serialization.DeserializingMessage;
import io.fluxcapacitor.javaclient.publishing.client.GatewayClient;
import io.fluxcapacitor.javaclient.publishing.correlation.DefaultCorrelationDataProvider;
import io.fluxcapacitor.javaclient.tracking.ConsumerConfiguration;
import io.fluxcapacitor.javaclient.tracking.client.DefaultTracker;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static io.fluxcapacitor.javaclient.FluxCapacitor.currentCorrelationData;
import static java.net.http.HttpRequest.BodyPublishers.ofByteArray;
@Slf4j
public class ForwardingWebConsumer implements AutoCloseable {
private final String host;
private final LocalServerConfig localServerConfig;
private final ConsumerConfiguration configuration;
private final HttpClient httpClient;
private final AtomicReference registration = new AtomicReference<>();
public ForwardingWebConsumer(LocalServerConfig localServerConfig, ConsumerConfiguration configuration) {
this.host = "http://localhost:" + localServerConfig.getPort();
this.localServerConfig = localServerConfig;
this.configuration = configuration;
this.httpClient = HttpClient.newHttpClient();
}
@Synchronized
public void start(FluxCapacitor fluxCapacitor) {
close();
GatewayClient gatewayClient = fluxCapacitor.client().getGatewayClient(MessageType.WEBRESPONSE);
BiConsumer gateway = (request, response) -> {
response.setTarget(request.getSource());
response.setRequestId(request.getRequestId());
response.setMetadata(response.getMetadata().with(currentCorrelationData()));
gatewayClient.append(Guarantee.NONE, response);
};
Consumer> consumer = messages -> messages.forEach(m -> {
Map correlationData = getCorrelationData(m);
try {
HttpRequest request = createRequest(m);
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray())
.whenComplete((r, e) -> {
if (e == null && r.statusCode() == 404 && localServerConfig.isIgnore404()) {
return;
}
gateway.accept(m, e == null ? toMessage(r, correlationData) : toMessage(e, correlationData));
});
} catch (Exception e) {
try {
gateway.accept(m, toMessage(e, correlationData));
} catch (Exception e2) {
log.error("Failed to create response message from exception", e2);
}
}
});
registration.getAndUpdate(r -> r == null ? DefaultTracker.start(consumer, MessageType.WEBREQUEST,
configuration, fluxCapacitor) : r);
}
protected Map getCorrelationData(SerializedMessage m) {
try {
return FluxCapacitor.getOptionally().map(FluxCapacitor::correlationDataProvider).orElse(
DefaultCorrelationDataProvider.INSTANCE).getCorrelationData(new DeserializingMessage(
m, type -> null, MessageType.WEBRESPONSE));
} catch (Exception e) {
log.error("Failed to get correlation data for request message", e);
return Collections.emptyMap();
}
}
protected HttpRequest createRequest(SerializedMessage m) {
try {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(new URI(host + WebRequest.getUrl(m.getMetadata())))
.method(WebRequest.getMethod(m.getMetadata()).name(),
m.getData().getValue().length == 0 ? HttpRequest.BodyPublishers.noBody() :
ofByteArray(m.getData().getValue()));
String[] headers = WebRequest.getHeaders(m.getMetadata()).entrySet().stream()
.filter(e -> !isRestricted(e.getKey()))
.flatMap(e -> e.getValue().stream().flatMap(v -> Stream.of(e.getKey(), v)))
.toArray(String[]::new);
if (headers.length > 0) {
builder.headers(headers);
}
if (m.getData().getFormat() != null) {
builder.header("Content-Type", m.getData().getFormat());
}
return builder.build();
} catch (Exception e) {
throw new IllegalStateException("Failed to create HttpRequest", e);
}
}
protected boolean isRestricted(String headerName) {
return Set.of("connection", "content-length", "expect", "host", "upgrade").contains(headerName.toLowerCase());
}
protected SerializedMessage toMessage(HttpResponse response,
Map correlationData) {
HttpHeaders headers = response.headers();
Metadata metadata = Metadata.of(correlationData)
.with(WebResponse.asMetadata(response.statusCode(), headers.map()));
return new SerializedMessage(new Data<>(response.body(), null, 0,
headers.firstValue("content-type").orElse(null)), metadata,
FluxCapacitor.generateId(), System.currentTimeMillis());
}
protected SerializedMessage toMessage(Throwable error,
Map correlationData) {
log.error("Failed to handle web request: " + error.getMessage() + ". Continuing with next request.", error);
return new SerializedMessage(
new Data<>("The request failed due to a server error".getBytes(), null, 0, "text/plain"),
Metadata.of(correlationData).with(WebResponse.asMetadata(500, Collections.emptyMap())),
FluxCapacitor.generateId(), System.currentTimeMillis());
}
@Override
public void close() {
registration.getAndUpdate(r -> {
if (r != null) {
r.cancel();
}
return null;
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy