Suche…


Bemerkungen

In diesem Abschnitt erhalten Sie einen Überblick darüber, was cxf ist und warum ein Entwickler es verwenden möchte.

Es sollte auch alle großen Themen in cxf erwähnen und auf die verwandten Themen verweisen. Da die Dokumentation für cxf neu ist, müssen Sie möglicherweise erste Versionen dieser verwandten Themen erstellen.

Grundlegender Webclient mit Provider

Für den Start benötigen wir eine Fabrik, die WebClients produziert.

public class ClientFactory {
    private Map<String, WebClient> cache = new HashMap<>();

    public enum RESTClient {
        PORTAL;
    }

    public WebClient fetchRestClient(RESTClient restClient) {

        if (this.cache.containsKey(restClient)) {
            return WebClient.fromClient(this.cache.get(rc));
        }

        if (RESTClient.enum.equals(rc)) {

            List<Object> providers = new ArrayList<Object>();
            providers.add(new GsonMessageBodyProvider());

            WebClient webClient = WebClient.create("https://blah.com", providers);

            HTTPConduit conduit = WebClient.getConfig(webClient).getHttpConduit();
            conduit.getClient().setReceiveTimeout(recieveTimeout);
            conduit.getClient().setConnectionTimeout(connectionTimout);

            this.cache.put(RESTClient.CAT_DEVELOPER_PORTAL.name(), webClient);
            return WebClient.fromClient(webClient);// thread safe
        }
    }
}
  • Zuerst erstellen wir eine Liste von Anbietern (werden später darauf zugreifen)
  • Als Nächstes erstellen wir einen neuen Webclient mit der statischen Factory "create ()". Hier können wir noch ein paar andere Dinge hinzufügen, wie Basic Auth Creds und Thread-Sicherheit. Für jetzt benutzen Sie einfach dieses.
  • Als nächstes ziehen wir den HTTPConduit heraus und legen die Timeouts fest. CXF wird mit Java-Basisklassen-Clients geliefert, Sie können jedoch Glassfish oder andere verwenden.
  • Zum Schluss zwischenspeichern wir den WebClient. Dies ist wichtig, da der Code bisher teuer zu erstellen ist. Die nächste Zeile zeigt, wie man es fadensicher macht. Beachten Sie, dass der Code auch aus dem Cache gezogen wird. Im Wesentlichen erstellen wir ein Modell des REST-Aufrufs und klonen es jedes Mal, wenn wir es brauchen.
  • Beachten Sie, was NICHT hier ist: Wir haben keine Parameter oder URLs hinzugefügt. Diese können hier hinzugefügt werden, aber das würde einen bestimmten Endpunkt ausmachen und wir wollen einen generischen. Außerdem werden der Anfrage keine Header hinzugefügt. Diese machen es nicht über den "Klon" hinaus, so dass sie später hinzugefügt werden müssen.

Jetzt haben wir einen WebClient, der bereit ist zu gehen. Lässt den Rest des Anrufs einrichten.

public Person fetchPerson(Long id) {
    long timer t = System.currentTimeMillis();
    Person person = null;
    try {
        wc = this.factory.findWebClient(RESTClient.PORTAL);
        wc.header(AUTH_HEADER, SUBSCRIPTION_KEY);
        wc.header(HttpHeaders.ACCEPT, "application/person-v1+json");

        wc.path("person").path("base");
        wc.query("id", String.valueOf(id));

        person = wc.get(Person.class);
    }
    catch (WebApplicationException wae) {
        // we wanna skip these. They will show up in the "finally" logs.
    }
    catch (Exception e) {
        log.error(MessageFormat.format("Error fetching Person: id:{0} ", id), e);
    }
    finally {
        log.info("GET HTTP:{} - Time:[{}ms] - URL:{} - Content-Type:{}", wc.getResponse().getStatus(), (System.currentTimeMillis() - timer), wc.getCurrentURI(), wc.getResponse().getMetadata().get("content-type"));
        wc.close();
    }
    return p;
}
  • Im "try" holen wir uns ab Werk einen WebClient. Dies ist eine neue "frostige".
  • Als nächstes setzen wir einige Header. Hier fügen wir eine Art Auth-Header und dann einen Accept-Header hinzu. Beachten Sie, dass wir einen benutzerdefinierten Accept-Header haben.
  • Als Nächstes werden Pfad und Abfragezeichenfolgen hinzugefügt. Denken Sie daran, dass es keine Reihenfolge für diese Schritte gibt.
  • Zum Schluss machen wir das "bekommen". Es gibt natürlich verschiedene Möglichkeiten, dies zu tun. Hier haben wir CXF, das JSON-Mapping für uns durchzuführen. Auf diese Weise müssen wir uns mit den WebApplicationExceptions befassen. Wenn wir also eine 404 erhalten, wird CXF eine Ausnahme auslösen. Beachten Sie, dass ich diese Ausnahmen esse, weil ich nur die Antwort im Protokoll protokolliere. Ich möchte jedoch eine andere Ausnahme erhalten, da sie wichtig sein könnte.
  • Wenn Ihnen diese Ausnahmebehandlung nicht gefällt, können Sie das Response-Objekt vom "get" zurückholen. Dieses Objekt enthält die Entität und den HTTP-Statuscode. Es wird NIE eine WebApplicationException ausgelöst. Der einzige Nachteil ist, dass Sie die JSON-Zuordnung nicht für Sie durchführen.
  • Schließlich haben wir in der "finally" -Klausel ein "wc.close ()". Wenn Sie das Objekt aus der get-Klausel erhalten, müssen Sie dies nicht wirklich tun. Es könnte jedoch etwas schief gehen, daher ist es eine gute Ausfallsicherheit.

Also, was ist mit diesem "Accept" -Header: application / person-v1 + json Wie kann CXF wissen, wie man ihn analysiert? CXF enthält einige eingebaute JSON-Parser, aber ich wollte Gson verwenden. Viele der anderen Implementierungen von Json-Parsern benötigen eine Anmerkung, nicht aber Gson. Es ist dumm einfach zu bedienen.

public class GsonMessageBodyProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

    private Gson gson = new GsonBuilder().create();
    
    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return StringUtils.endsWithIgnoreCase(mediaType.getSubtype(), "json");
    }

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
        try {
            return gson.fromJson(new BufferedReader(new InputStreamReader(entityStream, "UTF-8")), type);
        }
        catch (Exception e) {
            throw new IOException("Trouble reading into:" + type.getName(), e);
        }
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return StringUtils.containsIgnoreCase(mediaType.getSubtype(), "json");
    }

    @Override
    public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return 0;
    }

    @Override
    public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        try {
            JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8"));
            writer.setIndent("  ");
            gson.toJson(t, type, writer);
            writer.close();
        }
        catch (Exception e) {
            throw new IOException("Trouble marshalling:" + type.getName(), e);
        }
    }
}

Der Code ist unkompliziert. Sie implementieren zwei Schnittstellen: MessageBodyReader und MessageBodyWriter (oder nur eine) und fügen sie dann den "Providern" bei der Erstellung des WebClient hinzu. CXF findet es von dort heraus. Eine Option ist, in den Methoden "isReadable ()" "isWriteable ()" "true" zurückzugeben. Dadurch wird sichergestellt, dass CXF diese Klasse anstelle aller eingebauten verwendet. Die hinzugefügten Anbieter werden zuerst geprüft.

CXF für JAX-RS einrichten

Die Gläser für CXF JAX-RS befinden sich in Maven:

<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-rs-client -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-client</artifactId>
    <version>3.1.10</version>
</dependency>

Diese Gläser sind alles, was Sie brauchen, um es zum Laufen zu bringen:

cxf-rt-rs-client-3.1.10.jar
cxf-rt-transports-http-3.1.10.jar
cxf-core-3.1.10.jar
woodstox-core-asl-4.4.1.jar
stax2-api-3.1.4.jar
xmlschema-core-2.2.1.jar
cxf-rt-frontend-jaxrs-3.1.10.jar
javax.ws.rs-api-2.0.1.jar
javax.annotation-api-1.2.jar

Client-Filter

Ein guter Grund, Filter zu verwenden, ist die Protokollierung. Mit dieser Technik kann ein REST-Aufruf einfach protokolliert und zeitgesteuert werden.

public class RestLogger implements ClientRequestFilter, ClientResponseFilter {
    private static final Logger log = LoggerFactory.getLogger(RestLogger.class);

    // Used for timing this call.
    private static final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    private boolean logRequestEntity;
    private boolean logResponseEntity;

    private static Gson GSON = new GsonBuilder().create();

    public RestLogger(boolean logRequestEntity, boolean logResponseEntity) {
        this.logRequestEntity = logRequestEntity;
        this.logResponseEntity = logResponseEntity;
    }


    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        startTime.set(System.currentTimeMillis());
    }

    @Override
    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("HTTP:").append(responseContext.getStatus());
        sb.append(" - Time:[").append(System.currentTimeMillis() - startTime.get().longValue()).append("ms]");
        sb.append(" - Path:").append(requestContext.getUri());
        sb.append(" - Content-type:").append(requestContext.getStringHeaders().getFirst(HttpHeaders.CONTENT_TYPE.toString()));
        sb.append(" - Accept:").append(requestContext.getStringHeaders().getFirst(HttpHeaders.ACCEPT.toString()));
        if (logRequestEntity) {
            sb.append(" - RequestBody:").append(requestContext.getEntity() != null ? GSON.toJson(requestContext.getEntity()) : "none");
        }
        if (logResponseEntity) {
            sb.append(" - ResponseBody:").append(this.logResponse(responseContext));
        }
        log.info(sb.toString());
    }

    private String logResponse(ClientResponseContext response) {
        StringBuilder b = new StringBuilder();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        InputStream in = response.getEntityStream();
        try {
            ReaderWriter.writeTo(in, out);
            byte[] requestEntity = out.toByteArray();
            b.append(new String(requestEntity));
            response.setEntityStream(new ByteArrayInputStream(requestEntity));
        }
        catch (IOException ex) {
            throw new ClientHandlerException(ex);
        }
        return b.toString();
    }
}

Oben sehen Sie, dass die Anforderung abgefangen wird, bevor die Antwort gesendet und ein ThreadLocal Long festgelegt wird. Wenn die Antwort zurückgegeben wird, können wir die Anfrage und Antwort sowie alle relevanten Daten protokollieren. Das funktioniert natürlich nur für Gson-Antworten und solche, kann aber leicht geändert werden. Dies ist so eingerichtet:

List<Object> providers = new ArrayList<Object>();
providers.add(new GsonMessageBodyProvider());
providers.add(new RestLogger(true, true)); <------right here!

WebClient webClient = WebClient.create(PORTAL_URL, providers);

Das Protokoll sollte ungefähr so ​​aussehen:

7278 [main] INFO  blah.RestLogger - HTTP:200 - Time:[1391ms] - User:unknown - Path:https://blah.com/tmet/moduleDescriptions/desc?languageCode=en&moduleId=142 - Content-type:null - Accept:application/json - RequestBody:none - ResponseBody:{"languageCode":"EN","moduleId":142,"moduleDescription":"ECAP"}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow