cxf Samouczek
Rozpoczęcie pracy z cxf
Szukaj…
Uwagi
Ta sekcja zawiera przegląd tego, czym jest cxf i dlaczego deweloper może chcieć go użyć.
Powinien również wymieniać wszelkie duże tematy w cxf i zawierać linki do powiązanych tematów. Ponieważ Dokumentacja dla cxf jest nowa, może być konieczne utworzenie początkowych wersji tych pokrewnych tematów.
Podstawowy klient sieci Web z dostawcą
Na początek potrzebujemy fabryki, która produkuje 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
}
}
}
- Najpierw tworzymy listę dostawców (do nich później)
- Następnie tworzymy nowego webclienta za pomocą statycznej fabryki „create ()”. Tutaj możemy dodać kilka innych rzeczy, takich jak podstawowe uwierzytelnianie uwierzytelnienia i bezpieczeństwo wątków. Na razie skorzystaj z tego.
- Następnie wyciągamy HTTPConduit i ustawiamy limity czasu. CXF jest dostarczany z klientami klasy podstawowej Java, ale możesz używać Glassfish lub innych.
- Wreszcie buforujemy WebClient. Jest to ważne, ponieważ do tej pory tworzenie kodu jest kosztowne. Kolejny wiersz pokazuje, jak uczynić go wątkowo bezpiecznym. Zauważ, że w ten sposób również pobieramy kod z pamięci podręcznej. Zasadniczo tworzymy model wywołania REST, a następnie klonujemy go za każdym razem, gdy go potrzebujemy.
- Zauważ, że tutaj NIE ma: Nie dodaliśmy żadnych parametrów ani adresów URL. Można je tutaj dodać, ale stworzyłoby to konkretny punkt końcowy, a my chcemy ogólny. Ponadto do żądania nie dodano żadnych nagłówków. Nie przechodzą one poza „klon”, dlatego należy je dodać później.
Teraz mamy WebClient, który jest gotowy do pracy. Pozwala skonfigurować resztę połączenia.
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;
}
- Wewnątrz „prób” zabieramy WebClient z fabryki. To jest nowy „mroźny”.
- Następnie ustawiamy nagłówki. Tutaj dodajemy nagłówek Auth, a następnie nagłówek accept. Zauważ, że mamy niestandardowy nagłówek akceptacji.
- Kolejne jest dodanie ścieżki i ciągów zapytań. Pamiętaj, że nie ma kolejności tych kroków.
- Wreszcie robimy „get”. Oczywiście można to zrobić na kilka sposobów. Tutaj mamy CXF do wykonania dla nas mapowania JSON. Po wykonaniu tej czynności mamy do czynienia z wyjątkami WebApplicationException. Więc jeśli otrzymamy 404, CXF zgłosi wyjątek. Zauważ, że jem te wyjątki, ponieważ w końcu rejestruję odpowiedź. Chcę jednak uzyskać INNY wyjątek, ponieważ mogą one być ważne.
- Jeśli nie podoba ci się to przełączanie obsługi wyjątków, możesz odzyskać obiekt Response z „get”. Ten obiekt przechowuje encję i kod statusu HTTP. NIGDY nie zgłosi wyjątku WebApplicationException. Jedyną wadą jest to, że nie wykonuje dla ciebie mapowania JSON.
- Wreszcie w klauzuli „nareszcie” mamy „wc.close ()”. Jeśli zdobędziesz obiekt z klauzuli get, tak naprawdę nie musisz tego robić. Coś może się nie udać, więc jest to dobre zabezpieczenie przed awarią.
A co z nagłówkiem „accept”: application / person-v1 + json Skąd CXF będzie wiedział, jak go parsować? CXF ma wbudowane parsery JSON, ale chciałem użyć Gsona. Wiele innych implementacji parserów Jsona wymaga jakiejś adnotacji, ale nie Gsona. Jest głupi, łatwy w użyciu.
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);
}
}
}
Kod jest prosty. Wdrażasz dwa interfejsy: MessageBodyReader i MessageBodyWriter (lub tylko jeden), a następnie dodajesz go do „dostawców” podczas tworzenia WebClient. CXF wymyśla to stamtąd. Jedną z opcji jest zwrócenie „true” w metodach „isReadable ()” „isWriteable ()”. Zapewni to, że CXF używa tej klasy zamiast wszystkich wbudowanych. Dodani dostawcy zostaną najpierw sprawdzeni.
Konfigurowanie CXF dla JAX-RS
Słoiki do CXF JAX-RS znajdują się w 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>
Te słoiki są wszystkim, czego potrzebujesz, aby uruchomić:
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
Filtry klienta
Jednym dobrym powodem do korzystania z filtrów jest logowanie. Za pomocą tej techniki można łatwo zarejestrować i ustalić czas połączenia REST.
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();
}
}
Powyżej widać, że żądanie jest przechwytywane przed wysłaniem odpowiedzi i ustawieniem LongLocal Long. Po zwróceniu odpowiedzi możemy zarejestrować żądanie i odpowiedź oraz wszelkiego rodzaju istotne dane. Oczywiście działa to tylko w przypadku odpowiedzi Gson i takie, ale można je łatwo modyfikować. Jest to ustawione w ten sposób:
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);
Dostarczony dziennik powinien wyglądać mniej więcej tak:
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"}