Sök…


Anmärkningar

Det här avsnittet ger en översikt över vad cxf är och varför en utvecklare kanske vill använda den.

Den bör också nämna alla stora ämnen inom cxf och länka till relaterade ämnen. Eftersom dokumentationen för cxf är ny kan du behöva skapa initialversioner av relaterade ämnen.

Grundläggande webbklient med leverantör

För att komma igång behöver vi en fabrik som producerar WebClients.

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
        }
    }
}
  • Först skapar vi en lista över leverantörer (kommer till de senare)
  • Därefter skapar vi en ny webbklient med den statiska fabriken "create ()". Här kan vi lägga till några andra saker som Basic Auth-krediter och gängesäkerhet. För nu använder du bara den här.
  • Därefter drar vi ut HTTPConduit och ställer in timeouts. CXF levereras med Java-basklasser men du kan använda Glassfish eller andra.
  • Slutligen cache vi WebClient. Detta är viktigt eftersom koden hittills är dyr att skapa. Nästa rad visar hur man gör den trådlös. Observera att detta också är hur vi drar koden från cachen. I huvudsak gör vi en modell av REST-samtalet och klonerar sedan det varje gång vi behöver det.
  • Lägg märke till vad som INTE är här: Vi har inte lagt till några parametrar eller URL: er. Dessa kan läggas till här men det skulle göra en specifik slutpunkt och vi vill ha en generisk. Dessutom läggs inga rubriker till förfrågan. Dessa kommer inte förbi "klonen" så de måste läggas till senare.

Nu har vi en WebClient som är redo att gå. Låter ställa in resten av samtalet.

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;
}
  • Inuti "försöket" tar vi en WebClient från fabriken. Detta är en ny "frostig".
  • Därefter sätter vi några rubriker. Här lägger vi till någon typ av Auth-header och sedan en accept-header. Observera att vi har en anpassad accepthuvud.
  • Lägga till sökvägen och frågesträngarna kommer nästa. Kom ihåg att det inte finns någon ordning på dessa steg.
  • Slutligen gör vi "get". Det finns naturligtvis flera sätt att göra detta på. Här har vi CXF göra JSON-kartläggningen för oss. När vi gör det här måste vi ta itu med WebApplicationExceptions. Så om vi får en 404 kommer CXF att kasta ett undantag. Lägg märke till att jag äter dessa undantag eftersom jag bara loggar svaret till slut. Jag vill dock få något ANDRA undantag eftersom de kan vara viktiga.
  • Om du inte gillar den här växlingen med undantagshantering kan du få svar-objektet tillbaka från "get". Detta objekt innehåller enheten och HTTP-statuskoden. Det kommer ALDRIG att kasta en WebApplicationException. Den enda nackdelen är att det inte gör JSON-kartläggningen åt dig.
  • Slutligen, i "slutligen" -bestämmelsen har vi en "wc.close ()". Om du får objektet från get-klausulen behöver du inte göra det här. Något kan dock gå fel, så det är en bra misslyckad säkerhet.

Så vad med den "acceptera" rubriken: applikation / person-v1 + json Hur kommer CXF att veta hur man ska analysera det? CXF kommer med några inbyggda JSON-tolkare men jag ville använda Gson. Många av de andra implementeringarna av Json-tolkare behöver någon typ av kommentarer men inte Gson. Det är dumt lätt att använda.

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);
        }
    }
}

Koden är rakt fram. Du implementerar två gränssnitt: MessageBodyReader och MessageBodyWriter (eller bara ett) och lägger sedan till det till "leverantörerna" när du skapar WebClient. CXF räknar ut det därifrån. Ett alternativ är att returnera "sant" i metoden "isReadable ()" "isWriteable ()". Detta garanterar att CXF använder denna klass istället för alla inbyggda. De tillagda leverantörerna kommer att kontrolleras först.

Ställa in CXF för JAX-RS

Burkarna för CXF JAX-RS finns i 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>

Dessa burkar är allt du behöver för att få det igång:

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

Klientfilter

En bra anledning att använda filter är för loggning. Med hjälp av denna teknik kan ett REST-samtal enkelt loggas och tidsinställas.

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();
    }
}

Ovan kan du se förfrågan avlyssnas innan svaret skickas och en ThreadLocal Long är inställd. När svaret returneras kan vi logga in begäran och svaret och alla typer av relevant information. Naturligtvis fungerar detta bara för Gson-svar och sådant men kan enkelt ändras. Detta är inställt på detta sätt:

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);

Den medföljande loggen ska se ut så här:

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow