Novas APIs do Java EE 7 E a batalha contra a complexidade Helder da Rocha [email protected] Objetivos Falar um pouco de complexidade do software e apresentar três APIs do Java EE 7 que simplificam o processo de desenvolvimento Conteúdo Breve reflexão sobre complexidade e entropia JMS 2.0 APIs de Web Services (SOAP e REST) O futuro? 2 "A complexidade do software é uma propriedade essencial, e não acidental" F. Brooks Fonte: Grady Booch "Object Oriented Analysis and Design" Ilusão da simplicidade Fonte: Booch 6 Evolução das APIs Uma API deve ser a mais simples possível Contratos mais simples Menos burocracia Menos necessidade de documentação Mas… problema inerente ao software: complexidade Entropia sempre crescente Sucesso depende do controle da complexidade 2.0 2.0 1.0 1.0 3.0 1.0 Java EE 7 Objetivo sempre presente na evolução das APIs: Simplificar o uso! Menor curva de aprendizado Mais defaults, menos configuração Menos, menos, menos (até mesmo o JSF conseguiu ficar mais simples na Java EE 7 :-) Java Message Service 1.0 jndi.lookup(queueFactory) Queue Connection Factory API para enviar e receber mensagens usa classes e métodos diferentes para domínios Queue e Topic createConnection() Queue Connection createSession() Queue Sender Queue Session create() create() createMessage() Message send() receive() Queue jndi.lookup(queue) Queue Receiver Java Message Service 1.1 jndi.lookup(factory) Connection Factory API para enviar e receber mensagens usa classes e métodos iguais para domínios Queue e Topic createConnection() Connection createSession() Message Producer Session create() create() createMessage() Message send() receive() Destination jndi.lookup(destination) Message Consumer 10 Java Message Service 2.0 @Inject factory @Inject em vez de JNDI Connection Factory Menos código para enviar e receber mensagens createJMSContext() JMSProducer JMSConsumer JMSContext create() create() createMessage() Message send() Destination @Inject destination receive() Java Message Service 2.0 Menos código! Menos código! @Inject factory Connection Factory createJMSContext() JMSContext send() receive() Destination @Inject destination 11 Envio de mensagens JMS 2.0 12 Antes (JMS 1.1) public void sendJMS11(ConnectionFactory conFactory, Queue queue, String text) { try { Connection con = conFactory.createConnection(); try { Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(queue); TextMessage textMessage = session.createTextMessage(text); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { // handle exception } } JMS 2.0 public void sendJMS2(ConnectionFactory conFactory, Queue queue, String text) { try (JMSContext context = conFactory.createContext();) { context.createProducer().send(queue, text); } catch (JMSRuntimeException ex) { // handle exception } } Recebimento síncrono JMS 2.0 13 Antes (JMS 1.1) public void recJMS11(ConnectionFactory conFactory, Queue queue, String text) { try { Connection con = conFactory.createConnection(); try { Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageConsumer messageConsumer = session.createConsumer(queue); con.start(); TextMessage textMessage = (TextMessage)messageConsumer.receive(); this.messageContents = textMessage.getText(); } finally { connection.close(); } } catch (JMSException ex) { // handle exception } } JMS 2.0 public void recJMS2(ConnectionFactory conFactory, Queue queue, String text) { try (JMSContext context = conFactory.createContext();){ JMSConsumer consumer = context.createConsumer(queue); this.messageContents = consumer.receiveBody(String.class); } catch (JMSRuntimeException ex) { // handle exception } } 14 Pré-história do SOAP, parte 1 No princípio, Web Service eram simples de codificar em XML POST /ISBNService.jsp HTTP/1.0 Content-type: text/xml Content-length: 90 ISBNService.jsp 2 3 <chamada> <funcao> <nome>getPrice</nome> <param>2877142566</param> </funcao> </chamada> gera requisição 19.50 HTTP/1.1 200 OK Content-type: text/xml Content-length: 77 ISBNClient 1 gera resposta 2877142566 4 <resposta> <funcao> <param>19.50</param> </funcao> </resposta> ISBNQuery getPrice() BD Pré-história do SOAP parte 2 15 XML-RPC: padronização dos tags para operações, parâmetros, tipos, etc. Criado no grupo de discussões xml-dev em 1998 <methodCall> <methodName>getPrice</methodName> <params> Requisição <param> <value><string>2877142566</string></value> </param> </param> <methodResponse> </methodCall> <params> <param> <value><double>19.5</double></value> </param> Resposta </param> </methodResponse> 16 “Simples” requisição SOAP-RPC Padronização oficial Muito mais burocrática POST /xmlrpc-bookstore/bookpoint/BookstoreIF HTTP/1.0 Content-Type: text/xml; charset="utf-8" Content-Length: 585 SOAPAction: "" Mensagem <?xml version="1.0" encoding="UTF-8"?> (envelope) <env:Envelope SOAP xmlns:env="http://www.w3.org/2001/12/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2001/12/soap-encoding/" env:encodingStyle="http://www.w3.org/2001/12/soap-encoding/"> <env:Body> <ans1:getPrice xmlns:ans1="http://mybooks.org/wsdl"> <String_1 xsi:type="xsd:string">2877142566</String_1> </ans1:getPrice> </env:Body> </env:Envelope> Parâmetro (ISBN) Payload 17 Resposta SOAP-RPC HTTP/1.1 200 OK Content-Type: text/xml; charset="utf-8" SOAPAction: "" Date: Thu, 08 Aug 2002 01:48:22 GMT Server: Apache Coyote HTTP/1.1 Connector [1.0] Connection: close Mensagem (envelope) SOAP <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2001/12/soap-encoding/" xmlns:ns0="http://mybooks.org/types" env:encodingStyle="http://www.w3.org/2001/12/soap-encoding/"> <env:Body> <ans1:getPriceResponse xmlns:ans1="http://mybooks.org/wsdl"> <result xsi:type="xsd:decimal">19.50</result> </ans1:getPriceResponse> </env:Body> </env:Envelope> Resposta (Preço) Payload WSDL para descrever o serviço <?xml version="1.0" encoding="UTF-­‐8"?> <definitions name="BookstoreService" targetNamespace="http://mybooks.org/wsdl" xmlns:tns="http://mybooks.org/wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> Compare com a <types>...</types> mensagem SOAP <message name="BookstoreIF_getPrice"> mostrada <part name="String_1" type="xsd:string"/> </message> anteriormente <message name="BookstoreIF_getPriceResponse"> <part name="result" type="xsd:decimal"/> </message> <portType name="BookstoreIF"> <operation name="getPrice" parameterOrder="String_1"> <input message="tns:BookstoreIF_getPrice"/> <output message="tns:BookstoreIF_getPriceResponse"/> </operation> </portType> <binding ... > ...</binding> Informa onde está o serviço (endpoint) <service ... > ... </service> </definitions> 19 Simplificação com JAX-WS (escondendo a complexidade do XML) WEB-INF classes web.xml @WebService public class FilmeFacade { @PersistenceContext(unitName = "FilmesServiceSoap") EntityManager em; public List<Filme> getFilmes() { String jpql = "select filme from Filme filme"; Query query = em.createQuery(jpql); return (List<Filme>)query.getResultList(); } } Deploy FilmesServiceSoap.war @Entity public class Filme implements Serializable { @Id private Long id; private String titulo; private String diretor; private Long ano; private long duracao; private String imdb; } Endpoint: http://servidor/FilmesServiceSoap/FilmeFacadeService 20 Cliente SOAP public class FilmeClient { public static void main(String[] args) { FilmeFacadeService service = new FilmeFacadeService(); FilmeFacade proxy = service.getFilmeFacadePort(); listarFilmes(proxy.getFilmes()); } public static void listarFilmes(List<Filme> filmes) { for(Filme f : filmes) { System.out.println(f.getImdb()+": " + f.getTitulo() + "(" + f.getAno() + ")"); System.out.println(" " + f.getDiretor()); System.out.println(" " + f.getDuracao() + " minutos\n"); } } } $ java –jar FilmeClient.jar Com geração automática do código SOAP Ainda assim muito complexo: a complexidade migrou para a configuração tt0081505: The Shining(1980) Stanley Kubrick 144 minutos tt1937390: Nymphomaniac(2013) Lars von Trier 330 minutos tt0069293: Solyaris(1972) Andrei Tarkovsky 167 minutos tt1445520: Hearat Shulayim(2011) Joseph Cedar Cliente em container 21 Mais simples. Menos configuração @Named("filmesBean") CDI Managed Bean public class FilmesManagedBean { @WebServiceRef(wsdlLocation= "http://localhost:8080/FilmesServiceSoap/FilmeFacadeService?wsdl") private FilmeFacadeService service; private List<Filme> filmes; <h1>Lista de Filmes</h1> @PostConstruct <h:dataTable value="#{filmesBean.filmes}" var="filme”> public void init() { <h:column> FilmeFacade proxy = <f:facet name="header">IMDB</f:facet> <a href="http://www.imdb.com/title/#{filme.imdb}"> service.getFilmeFacadePort(); #{filme.imdb}</a> this.filmes = proxy.getFilmes(); </h:column> } <h:column> … <f:facet name="header">Título</f:facet> } #{filme.titulo} </h:column> ... </h:dataTable> JSF Facelets RESTful Web Services REST ~= Infraestrutura de um website recursos (páginas), links, hierarquia representações de dados e tipos (URIs, MIME) vocabulário de operações do protocolo HTTP (GET, POST, …) URIs ~= objetos pais.estado.cidade == http://servidor/aplicacao/pais/estado/cidade/ Representações XML, JSON, CSV, etc. Métodos HTTP permitem operações CRUD Create: Retrieve: Update: Delete: POST /pais/estado (<estado>SP</estado>, no corpo) GET /pais/estado/SP (Retrieve All com GET /pais/estado) PUT /pais/estado/PB DELETE /pais/estado/PB 22 JAX-RS 23 JAX-RS : API Java para RESTful WebServices Componentes Subclasse de Application: configuração Root resource class: fachada de serviços Resource method: associados a métodos HTTP (@GET, @POST, etc.) Providers: produzem ou consomem representações de entidades em outros formatos (ex: XML, JSON) JAX-RS usa anotações para configurar os componentes @ApplicationPath na subclasse de Application @Path nos Root resource classes @GET, @POST, etc e @Path nos Resource methods @Produces, @Consumes nos Providers @Context para injetar diversos tipos de contexto do ambiente Web Path templates e @PathParam 24 @Path templates @Path("/filmes/{imdb}") Aceita por exemplo: http://abc.com/war/app/filmes/tt0066921 Parâmetros @PathParam @Path("/filmes/{imdb}") public class FilmesIMDBResource { @GET @Produces("text/xml") public Filme getFilme(@PathParam("imdb") String codigoIMDB) { return entity.getFilmeByIMDBCode(codigoIMDB); } } Diferentes representações de objetos Classes class Telefone { int ddd; int numero; } class Pessoa { int numero; String nome; Telefone[] telefones = new Telefone[3]; } Objetos em Java Pessoa pessoa = new Pessoa(); pessoa.numero = 145; pessoa.nome = "Jeeves"; pessoa.telefone[0] = new Telefone(); pessoa.telefone[0].ddd = 11; pessoa.telefone[0].numero = 34567890; Representação em XML <pessoa xmlns="http://pessoa" numero="145"> <nome>Jeeves</nome> <telefone> <ddd>11</ddd> <numero>34567890</numero> </telefone> </pessoa> application/xml 25 Representação em JSON { "@numero": 145, "nome": "Jeeves", [ "telefone": { "ddd": 11, "numero": 34567890 }] } application/json 26 JAXB Java API for XML Binding Mapeia classes Java a XML Schema Classes mapeadas a XML Schema (ferramentas xjc e schemagen) Objetos mapeados a documentos XML (através da API javax.xml.bind e operações de serialização em XML (marshalling e unmarshalling) XML Schema xjc Binding Classes Java schemagen Documentos (instâncias XML) unmarshal() Marshalling marshal() Objetos (instâncias Java) JAXB: exemplo de classe anotada 27 @XmlSchema(namespace = "http://filmes.argonavis.com/") package com.argonavis.filmes.generated; @XmlRootElement public class Sala implements Serializable { private List<Assento> assentos; private String nome; private int lotacao; @XmlElementWrapper @XmlElement(name="assento") public List<Assento> getAssentos() { return assentos; } … } Sem @XmlElementWrapper <sala> <assentos>…</assentos> <assentos>…</assentos> </sala> Com @XmlElementWrapper <sala> <assentos> <assento>…</assento> <assento>…</assento> </assentos> </sala> JAXB Marshal / Unmarshal 28 Unmarshalling = XML àJava JAXBContext jc = JAXBContext.newInstance( "com.argonavis.filmes.gen" ); Unmarshaller u = jc.createUnmarshaller(); Filme filme = (Filme)u.unmarshal( new File( "tt1937390.xml" ) ); Marshalling = Java àXML JAXBContext jc = JAXBContext.newInstance( "com.argonavis.flmes.gen" ); Filme filme = ObjectFactory.createFilme(); filme.setAno(2014); // alterando objeto Marshaller m = jc.createMarshaller(); m.marshal( filme, System.out ); 29 JSON Binding! Substituir o provedor JAXB default por EclipseLink MOXy: javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory Configuração de JAXBContext Map properties = new HashMap(); props.put("eclipselink.media-type", "application/json"); JAXBContext ctx = JAXBContext.newInstance(new Class[] { Produto.class }, props); JSON à Java UnMarshaller u = ctx.createUnMarshaller(); Produto produto= u.unmarshal(new StreamSource(“produto123.json”)); Java à JSON Marshaller m = ctx.createMarshaller(); m.marshal(produto, System.out); Haverá suporte nativo a JSON binding no Java EE 8 (2016) Cliente REST com java.net 30 Cliente HTTP! URL url = new URL("http://localhost:8080/ctx/app/imdb/tt0066921"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/xml"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Erro : " + conn.getResponseCode()); } BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); String linha = br.readLine(); System.out.println("Dados recebidos: " + linha); conn.disconnect(); JAXBContext jc = JAXBContext.newInstance(Filme.class); Unmarshaller u = jc.createUnmarshaller(); Filme filme = (Filme) u.unmarshal(new StringReader(linha)); System.out.println(filme.getIMDB()); … 31 Cliente Jersey* Ainda mais simples ClientConfig config = new DefaultClientConfig(); Client client = Client.create(config); URI baseURI = UriBuilder.fromUri("http://localhost:8080/ctx").build(); WebResource service = client.resource(baseURI); Filme filme = service.path("app") .path("filme/imdb/tt0066921") .accept(MediaType.APPLICATION_XML) .get(Filme.class); System.out.println(filme.getIMDB()); System.out.println(filme.getTitulo()); System.out.println(filme.getDiretor()); * Existem outras alternativas. Veja RESTEasy 32 Cliente mobile (iPhone) REST é melhor alternativa para Web Services que fazem integração como outras plataformas 1) Cliente REST escrito em Objective-C rodando em iOS 7 gera requisição GET http://192.168.1.25/festival/webapi/filme 2) Glassfish gera resposta HTTP com lista de filmes em JSON 3) iPhone extrai dados e preenche UITableView Glassfish 4.0 O futuro Java EE 8 promete uma vida menos complexa API de segurança unificada API de transações unificada CDI com papel central em todas as aplicações JSON binding nativo Melhor integração com WebSockets Mas, ... A entropia continua a crescer... + linguagens + bugs + problemas + ameaças + horizontes + iOT + ... a luta continua! Fonte: http://tinyurl.com/7zlytsm "the singularity is near!" Ray Kurzweil [email protected]