/** * Copyright (C) 2010 Talend Inc. - www.talend.com */ package client; import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Properties; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.search.client.SearchConditionBuilder; import common.advanced.Person; import common.advanced.PersonCollection; import common.advanced.PersonInfo; import common.advanced.PersonService; /** * Example showing the interaction between HTTP-centric and proxy based RESTful clients and JAX-RS server * providing multiple services (PersonService and SearchService) */ public final class RESTClient { private static final String PORT_PROPERTY = "http.port"; private static final int DEFAULT_PORT_VALUE = 8080; private static final String HTTP_PORT; static { Properties props = new Properties(); try { props.load(RESTClient.class.getResourceAsStream("/client.properties")); } catch (Exception ex) { throw new RuntimeException("client.properties resource is not available"); } HTTP_PORT = props.getProperty(PORT_PROPERTY); } int port; public RESTClient() { this(getPort()); } public RESTClient(int port) { this.port = port; } /** * PersonService provides information about all the persons it knows about, about individual persons and * their relatives : - ancestors - parents, grandparents, etc - descendants - children, etc - partners * Additionally it can help with adding the information about new children to existing persons and update * the age of the current Person */ public void usePersonService() throws Exception { System.out.println("Using a Web Client..."); // A single web client will be used to retrieve all the information final String personServiceURI = "http://localhost:" + port + "/services/personservice/main"; WebClient wc = WebClient.create(personServiceURI); // Get the list of all persons System.out.println("Getting the XML collection of all persons in a type-safe way..."); wc.accept(MediaType.APPLICATION_XML); List<Person> persons = getPersons(wc); // Get individual persons using JSON System.out.println("Getting individual persons using JSON..."); wc.reset().accept(MediaType.APPLICATION_JSON); for (Person person : persons) { // Move forward, for example, given that web client is currently // positioned at // personServiceURI and a current person id such as 4, wc.path(id) // will point // the client to "http://localhost:8080/personservice/main/4" wc.path(person.getId()); // Read the stream InputStream is = wc.get(InputStream.class); System.out.println(IOUtils.toString(is)); // Move only one path segment back, to // "http://localhost:8080/personservice/main" // Note that if web client moved few segments forward from the base // personServiceURI // then wc.back(true) would bring the client back to the baseURI wc.back(false); } // Get Person with id 4 : System.out.println("Getting info about Fred..."); // wc.reset() insures the current path is reset to the base // personServiceURI and // the headers which may have been set during the previous invocations // are also reset. wc.reset().accept(MediaType.APPLICATION_XML); wc.path("4"); getPerson(wc); System.out.println("Getting info about Fred's mother"); wc.path("mother"); getPerson(wc); System.out.println("Getting info about Fred's father"); wc.back(false).path("father"); getPerson(wc); System.out.println("Getting info about Fred's partner"); wc.back(false).path("partner"); getPerson(wc); // Get info about Fred's ancestors, descendants and children wc.reset().accept(MediaType.APPLICATION_XML); wc.path("4"); System.out.println("Getting info about Fred's ancestors"); wc.path("ancestors"); getPersons(wc); System.out.println("Getting info about Fred's descendants"); wc.back(false).path("descendants"); getPersons(wc); System.out.println("Getting info about Fred's children"); wc.back(false).path("children"); getPersons(wc); System.out.println("Fred and Catherine now have a child, adding a child info to PersonService"); Person child = new Person("Harry", 1); Response response = wc.reset().path("4").path("children").post(child); // 201 status and the Location header pointing to // a newly created (child) Person resource is expected if (response.getStatus() != 201) { throw new RuntimeException("No child resource has been created"); } // Follow the Location header pointing to a new child resource // and get the information, confirm it is Harry String location = response.getMetadata().getFirst("Location").toString(); getPerson(WebClient.create(location)); System.out.println("Getting an up to date info about Fred's children"); getPersons(wc); System.out.println("Fred has become 40, updating his age"); // WebClient is currently pointing to personServiceURI + "/4" + // "/children" wc.back(false); // Back to personServiceURI + "/4" wc.path("age").type("text/plain"); // Trying to update the age to the wrong value by mistake Response rc = wc.put(20); // HTTP Bad Request status is expected if (rc.getStatus() != 400) { throw new RuntimeException("Fred has become older, not younger"); } rc = wc.put(40); // 204 (No Content) is a typical HTTP PUT response if (rc.getStatus() != 204) { throw new RuntimeException("Impossible to update Fred's age"); } System.out.println("Getting up to date info about Fred"); // WebClient is currently pointing to personServiceURI + "/4" + "/age" wc.back(false); // Back to personServiceURI + "/4" getPerson(wc); // finally, do a basic search: wc.reset().path("find").accept(MediaType.APPLICATION_XML); wc.query("name", "Fred", "Lorraine"); printPersonCollection(wc.get(PersonCollection.class)); System.out.println("Using PATCH..."); WebClient.getConfig(wc).getRequestContext().put("use.async.http.conduit", true); String patch = wc.reset().invoke("PATCH", null, String.class); System.out.println("Patch: " + patch); } /** * SearchService is a service which shares the information about Persons with the PersonService. * It lets users search for individual people using simple or complex search expressions. * The interaction with this service also verifies that the JAX-RS server is capable of supporting multiple * root resource classes */ private void useSearchService() throws Exception { System.out.println("Searching..."); WebClient wc = WebClient.create("http://localhost:" + port + "/services/personservice/search"); WebClient.getConfig(wc).getHttpConduit().getClient().setReceiveTimeout(10000000L); wc.accept(MediaType.APPLICATION_XML); // Moves to "/services/personservice/search" wc.path("person"); SearchConditionBuilder builder = SearchConditionBuilder.instance(); System.out.println("Find people with the name Fred or Lorraine:"); String query = builder.is("name").equalTo("Fred").or() .is("name").equalTo("Lorraine") .query(); findPersons(wc, query); System.out.println("Find all people who are no more than 30 years old"); query = builder.is("age").lessOrEqualTo(30) .query(); findPersons(wc, query); System.out.println("Find all people who are older than 28 and whose father name is John"); query = builder.is("age").greaterThan(28) .and("fatherName").equalTo("John") .query(); findPersons(wc, query); System.out.println("Find all people who have children with name Fred"); query = builder.is("childName").equalTo("Fred") .query(); findPersons(wc, query); //Moves to "/services/personservice/personinfo" wc.reset().accept(MediaType.APPLICATION_XML); wc.path("personinfo"); System.out.println("Find all people younger than 40 using JPA2 Tuples"); query = builder.is("age").lessThan(40).query(); // Use URI path component to capture the query expression wc.path(query); Collection<? extends PersonInfo> personInfos = wc.getCollection(PersonInfo.class); for (PersonInfo pi : personInfos) { System.out.println("ID : " + pi.getId()); } } private void findPersons(WebClient wc, String searchExpression) { wc.resetQuery(); wc.query("_s", searchExpression); PersonCollection persons = wc.get(PersonCollection.class); printPersonCollection(persons); } private void printPersonCollection(PersonCollection persons) { for (Person person : persons.getList()) { System.out.println("Found : " + person.getName()); } } /** * This function uses a proxy which is capable of transforming typed invocations into proper HTTP calls * which will be understood by RESTful services. This works for subresources as well. Interfaces and * concrete classes can be proxified, in the latter case a CGLIB runtime dependency is needed. CXF JAX-RS * proxies can be configured the same way as HTTP-centric WebClients and response status and headers can * also be checked. HTTP response errors can be converted into typed exceptions. */ public void useSimpleProxy() { String webAppAddress = "http://localhost:" + port + "/services/personservice"; PersonService proxy = JAXRSClientFactory.create(webAppAddress, PersonService.class); new PersonServiceProxyClient(proxy).useService(); } private Person getPerson(WebClient wc) { Person person = wc.get(Person.class); System.out.println("ID " + person.getId() + " : " + person.getName() + ", age : " + person.getAge()); return person; } private List<Person> getPersons(WebClient wc) { // Can limit rows returned with 0-based start index and size (number of // records to return, -1 for all) // default (0, -1) returns everything // wc.query("start", "0"); // wc.query("size", "2"); PersonCollection collection = wc.get(PersonCollection.class); // Can call wc.getResponse() to see response codes List<Person> persons = collection.getList(); System.out.println("Size: " + persons.size()); for (Person person : persons) { System.out.println("ID " + person.getId() + " : " + person.getName() + ", age : " + person.getAge()); } return persons; } public static void main(String[] args) throws Exception { RESTClient client = new RESTClient(); // uses CXF JAX-RS WebClient client.usePersonService(); // uses a basic proxy client.useSimpleProxy(); // uses CXF JAX-RS WebClient to do the advanced search client.useSearchService(); } private static int getPort() { try { return Integer.valueOf(HTTP_PORT); } catch (NumberFormatException ex) { // ignore } return DEFAULT_PORT_VALUE; } }