Put your systems to REST       Guilherme Silveira       @guilhermecaelum
Guilherme Silveira                  guilherme.silveira@caelum.com.br2002   guj.com.br2003   vraptor2004   caelum.com.br200...
banco.com    calendario.comviagem.com   empresa.com
CORBAparaíso?
EJBparaíso?
SOAPparaíso?
o que é REST?
qual o futuro da integração na      web?
representação de uma viagem<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight>  <information>    <from>sao paul...
service locator ao integrar:                     acoplamento++                           unrest                           ...
service locator ao integrar:                     acoplamento++                           unrest                           ...
representação em REST<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight>  <information>    <from>sao paulo</fro...
Rest Client: hipermídiaflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment").  ...
alterando o serviço<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight>  <information>    <from>sao paulo</from>...
dependency injection em integração:                        acoplamento--                            restrips              ...
dependency injection em integração:                        acoplamento--                            restrips              ...
trip = recurso        http://kayak.com/f/574XR4payment = recurso        any_uri_unknown_at_compile_time
eu nunca viajo sozinho
meu amigo  ricky
Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLin...
Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLin...
Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLin...
calendar: integrando sistemasmyself = Client.at(http://users.calendar.com)                                .with(auth).get(...
calendar: integrando sistemasmyself = Client.at(http://users.calendar.com)                                .with(auth).get(...
calendar: outros exemplosme.link("calendar").patch(representacao_aniversarios)me.link("calendar").patch(represetancao_hotel)
e daí?
e daí?Atualizações na viagem ==> refletem aquiAtualizações no hotel ==> refletem aquiAtualizações no encontro ==> refletem...
e daí?                          gre!                      nte                 e ,i              us         n ãoAtualizaçõe...
o que mais?
o que mais?Remove ==> Cancela a viagemRemove ==> Cancela o hotelRemove ==> Email para colegas
o que mais?                                   liz e!                              nt ra                          esce     ...
mas qual o formato dopagamento ou calendário?
#json, #xml,#soap #etc?
qual #json, #xml      #etc?
exercise
quem é este?
quem é essa?
fácil? e ela?
fácil? e ela?                       lh a!                      fi                in ha          é m   n ão
conteúdo sem semântica    não tem valor!
qual “formato”?
qual “formato”?micro formats, media types, rdf etc
qual “formato”?                              nk s!                         er li                      hyp               ts...
integração na web
Put your server to REST    leonard richardsons model
Server Maturity              1 uri, 1 http verb/services.do?action=install&...
Server Maturity                  1 uri, 1 http verb/services.do?action=install&...@Path("/services")public class Services ...
Server Maturity                1 uri, 1 http verbpublic class InstallService {  public Response execute() {    return Resp...
But?
1 uri, 1 http verb/services.do?action=install&... I know them all beforehand.
1 uri, 1 http verb/services.do?action=install&...Any change will break all our clients!
a priori knowledge
a priori knowledge        =
a priori knowledge        =    coupling++
Server Maturity           Multiple uris, 1 http verb/install?...
Server Maturity             Multiple uris, 1 http verb/install?... @Path("/services") public class Services {   @GET @Path...
but...
GET hellGET should not imply in  undesireable effects
POST hellGET could help us with        cache!
Server Maturity  Multiple uris, multiple verbs         POST /cloud GET /cloud/1547437/softwarePOST /cloud/1547437/software
rails + restfulie  rubyonrails.org
rails + restfulie                   rubyonrails.orgclass SoftwaresController < ApplicationController    acts_as_restfulie ...
JAX-RS@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  public Response install(Soft...
JAX-RS  @DELETE @Path("{id}")  public Response uninstall(@PathParam("id") Long id) {    Software software = SoftwareReposi...
Uniform Interface++   Resources++
Http verbs examplePOST createsPUT replacesPATCH updatesDELETE removesGET retrievesOPTIONS tells me what i can do...
Is a Restful service   a cute CRUD?
yes
but there is more!
@XmlRootElement@XmlType(propOrder= {"id", "host", "softwares", "links"})public class Machine {  private final all variable...
@XmlRootElement@XmlType(propOrder= {"id", "host", "softwares", "links"})public class Machine {  private final all variable...
@Path("/machines")public class MachineResource {        MachineResource  @Path("{id}/softwares")  public SoftwareResource ...
@Path("/machines")public class MachineResource {        MachineResource  @Path("{id}/softwares")  public SoftwareResource ...
Server Modelaccording toLeonard Richardson, 2008                           one uri + one verb
Server Modelaccording toLeonard Richardson, 2008                               Ugly                           one uri + on...
Server Modelaccording toLeonard Richardson, 2008                           multiple                            uris       ...
Server Modelaccording toLeonard Richardson, 2008                                      Less ugly                           ...
Server Modelaccording toLeonard Richardson, 2008                                       multiple                           ...
Server Modelaccording toLeonard Richardson, 2008                           cute CRUD                                      ...
Server Model                      restaccording toLeonard Richardson, 2008        hypermedia                              ...
JAX-RS 2 wishlist
guj
vraptor 0, 2, 3
java keynotes
exemplosvraptor.org
1. repetições?@Path("/services")public class Services {  @GET  public Response services(    @QueryParam("action") String a...
você quer fazer copy+paste?
repetir = inferno
Convention over Configuration@Resourcepublic class Services {		 private final ServiceFactory factory;		 public Services(Ser...
yes!
yes!
2. TDD: hard to test@Path("/products")public class Products {                                   coupled to the  @GET      ...
3. Content negotiation na unha@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  publ...
4. URI coupling                                   writing the URI once...  @GET @Path("/softwares/{id}")  public Response ...
4. URI coupling                                  writing the URI again                                       several times...
4. URI coupling                                            code@Path("/softwares")public class SoftwareResource {  @POST @...
repetir = inferno
5. Parameter list@Path("/machines")public class MachineResource {  @Path("{id}/softwares")  public SoftwareResource softwa...
repetir = inferno
java         =string oriented?
java          =annotation oriented?
Cliente REST
Response response = client.at    ("http://localhost:9998/user/574").get();        6. Client internal DSLs
Response response = client.at    ("http://localhost:9998/user/574").get();User user = response.getResource();System.out.pr...
User user = response.getResource();System.out.println("user: " + user.getName());Link link = resource(user).getLink("machi...
Link link = resource(user).getLink("machine");response = link.follow().post(new Machine());double amount = resource(user)....
double amount = resource(user).refresh().                                        getAmountDue();link = resource(user).getL...
link = resource(user).getLink("payment");Payment payment = new Payment(amount);response = link.follow().post(payment);Syst...
link = resource(user).getLink("payment");Payment payment = new Payment(amount);response = link.follow().post(payment);Syst...
bottom up  design by committe      mime type    microformato
hypermídiaviabilizando a integração através da web
vraptor mvc based REST
Arun Gupta   jersey vraptor 3
vraptor somos nozes
@guilhermecaelumobrigado
Crie seu sistema REST com JAX-RS e o futuro
Crie seu sistema REST com JAX-RS e o futuro
Crie seu sistema REST com JAX-RS e o futuro
Crie seu sistema REST com JAX-RS e o futuro
Upcoming SlideShare
Loading in …5
×

Crie seu sistema REST com JAX-RS e o futuro

3,726 views

Published on

Palestra de Guilherme Silveira durante o Javaone Brazil no keynote hall.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,726
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
42
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 1 uri. 1 http verb\n
  • implementing a specific response\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Crie seu sistema REST com JAX-RS e o futuro

    1. 1. Put your systems to REST Guilherme Silveira @guilhermecaelum
    2. 2. Guilherme Silveira guilherme.silveira@caelum.com.br2002 guj.com.br2003 vraptor2004 caelum.com.br2009 restfulie2010 tectura.com.br
    3. 3. banco.com calendario.comviagem.com empresa.com
    4. 4. CORBAparaíso?
    5. 5. EJBparaíso?
    6. 6. SOAPparaíso?
    7. 7. o que é REST?
    8. 8. qual o futuro da integração na web?
    9. 9. representação de uma viagem<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight> <information> <from>sao paulo</from> <to>seoul</to> </information>  <value>900.00</value></flight>
    10. 10. service locator ao integrar: acoplamento++ unrest pay unresttrips.comguilherme
    11. 11. service locator ao integrar: acoplamento++ unrest pay unresttrips.comguilherme
    12. 12. representação em REST<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight> <information> <from>sao paulo</from> <to>seoul</to> </information>  <value>900.00</value> <link rel="payment" href="http://resttrips.com/payment/custom"/</flight>
    13. 13. Rest Client: hipermídiaflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value);
    14. 14. alterando o serviço<?xml version="1.0" encoding="UTF-8" standalum="yes"?><flight> <information> <from>sao paulo</from> <to>seoul</to> </information>  <value>900.00</value> <link rel="payment" href="http://paysite.com/custom"/></flight>
    15. 15. dependency injection em integração: acoplamento-- restrips pay resttrips.com guilherme paysite.com
    16. 16. dependency injection em integração: acoplamento-- restrips pay resttrips.com guilherme paysite.com
    17. 17. trip = recurso http://kayak.com/f/574XR4payment = recurso any_uri_unknown_at_compile_time
    18. 18. eu nunca viajo sozinho
    19. 19. meu amigo ricky
    20. 20. Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);// send the payment link to another part of the webflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);
    21. 21. Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);// send the payment link to another part of the webflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);
    22. 22. Rest Client: compartilhando uma viagemflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);// send the payment link to another part of the webflight = Client.at(http://resttrips.com/f/574XR4).get();confirmation = flight.getLink("payment"). patch(cardInformation, value/2);
    23. 23. calendar: integrando sistemasmyself = Client.at(http://users.calendar.com) .with(auth).get();myself.link("calendar").patch(flight.link("self"));
    24. 24. calendar: integrando sistemasmyself = Client.at(http://users.calendar.com) .with(auth).get();myself.link("calendar").patch(flight.link("self"));
    25. 25. calendar: outros exemplosme.link("calendar").patch(representacao_aniversarios)me.link("calendar").patch(represetancao_hotel)
    26. 26. e daí?
    27. 27. e daí?Atualizações na viagem ==> refletem aquiAtualizações no hotel ==> refletem aquiAtualizações no encontro ==> refletem aqui
    28. 28. e daí? gre! nte e ,i us n ãoAtualizações na viagem ==> refletem aquiAtualizações no hotel ==> refletem aquiAtualizações no encontro ==> refletem aqui
    29. 29. o que mais?
    30. 30. o que mais?Remove ==> Cancela a viagemRemove ==> Cancela o hotelRemove ==> Email para colegas
    31. 31. o que mais? liz e! nt ra esce e, d s tr rq ue o n ãoRemove ==> Cancela a viagemRemove ==> Cancela o hotelRemove ==> Email para colegas
    32. 32. mas qual o formato dopagamento ou calendário?
    33. 33. #json, #xml,#soap #etc?
    34. 34. qual #json, #xml #etc?
    35. 35. exercise
    36. 36. quem é este?
    37. 37. quem é essa?
    38. 38. fácil? e ela?
    39. 39. fácil? e ela? lh a! fi in ha é m n ão
    40. 40. conteúdo sem semântica não tem valor!
    41. 41. qual “formato”?
    42. 42. qual “formato”?micro formats, media types, rdf etc
    43. 43. qual “formato”? nk s! er li hyp ts e a fo rm icro mmicro formats, media types, rdf etc
    44. 44. integração na web
    45. 45. Put your server to REST leonard richardsons model
    46. 46. Server Maturity 1 uri, 1 http verb/services.do?action=install&...
    47. 47. Server Maturity 1 uri, 1 http verb/services.do?action=install&...@Path("/services")public class Services {  @GET  public Response services(    @QueryParam("action") String action) {    ServiceFactory factory = new ServiceFactory();    Service service = factory.getServiceFor(action);    return service.execute();  }}
    48. 48. Server Maturity 1 uri, 1 http verbpublic class InstallService {  public Response execute() {    return Response.ok()             .type("application/xml")             .entity("<service>...</service>")             .build();  }}
    49. 49. But?
    50. 50. 1 uri, 1 http verb/services.do?action=install&... I know them all beforehand.
    51. 51. 1 uri, 1 http verb/services.do?action=install&...Any change will break all our clients!
    52. 52. a priori knowledge
    53. 53. a priori knowledge =
    54. 54. a priori knowledge = coupling++
    55. 55. Server Maturity Multiple uris, 1 http verb/install?...
    56. 56. Server Maturity Multiple uris, 1 http verb/install?... @Path("/services") public class Services {   @GET @Path("install")   public Response install() {     return new InstallService().execute();   }   @GET @Path("uninstall")   public Response uninstall() {     return new UninstallService().execute();   }
    57. 57. but...
    58. 58. GET hellGET should not imply in undesireable effects
    59. 59. POST hellGET could help us with cache!
    60. 60. Server Maturity Multiple uris, multiple verbs POST /cloud GET /cloud/1547437/softwarePOST /cloud/1547437/software
    61. 61. rails + restfulie rubyonrails.org
    62. 62. rails + restfulie rubyonrails.orgclass SoftwaresController < ApplicationController    acts_as_restfulie  respond_to :xml, :json    def create    @item = Item.create(params[:item])    respond_with @item, :status => :created  end    def show    @item = Item.find(params[:id])    respond_with @item  endend
    63. 63. JAX-RS@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  public Response install(Software software) {    software = SoftwareRepository.register(software);    long id = software.getId();    URI uri = UriBuilder.fromPath("/softwares/" + id)                                       .build();    software.install();    return Response.created(uri).build();  }
    64. 64. JAX-RS  @DELETE @Path("{id}")  public Response uninstall(@PathParam("id") Long id) {    Software software = SoftwareRepository.retrieve(id);    software.uninstall();    return Response.ok().build();  }
    65. 65. Uniform Interface++ Resources++
    66. 66. Http verbs examplePOST createsPUT replacesPATCH updatesDELETE removesGET retrievesOPTIONS tells me what i can do...
    67. 67. Is a Restful service a cute CRUD?
    68. 68. yes
    69. 69. but there is more!
    70. 70. @XmlRootElement@XmlType(propOrder= {"id", "host", "softwares", "links"})public class Machine {  private final all variables here;  @XmlElement(name="link", namespace="http://www.w3.org/2005/Atom")  public List<Link> getLinks() {    return Arrays.asList(      Link.to("/machines/" + getId(), "self"),      Link.to("/machines/" + getId() + "/softwares", "softwares")    );  }  @XmlElementWrapper(name="softwares")  @XmlElement(name="software")  public List<Software> getSoftwares() {    return softwares;  }  public void install(Software software) {    getSoftwares().add(software);  }  public void uninstall(Software software) {    getSoftwares().remove(software);  }} Machine.java
    71. 71. @XmlRootElement@XmlType(propOrder= {"id", "host", "softwares", "links"})public class Machine {  private final all variables here;  @XmlElement(name="link", namespace="http://www.w3.org/2005/Atom")  public List<Link> getLinks() {    return Arrays.asList(      Link.to("/machines/" + getId(), "self"),      Link.to("/machines/" + getId() + "/softwares", "softwares")    );  }  @XmlElementWrapper(name="softwares")  @XmlElement(name="software")  public List<Software> getSoftwares() {    return softwares;  }  public void install(Software software) {    getSoftwares().add(software);  }  public void uninstall(Software software) {    getSoftwares().remove(software);  }} Machine.java
    72. 72. @Path("/machines")public class MachineResource { MachineResource @Path("{id}/softwares") public SoftwareResource softwares(@PathParam("id") Long id) { Machine machine = new MachineRepository().retrieve(id); if (machine != null) { SoftwareResource softwareResource = new SoftwareResource(); softwareResource.setMachine(machine); return softwareResource; } throw new WebApplicationException(404); } @POST @Consumes("application/xml") public Response create(Machine machine) { Long id = new MachineRepository().save(machine); return Response.created(UriBuilder.fromPath("/" + id).build()).build(); } @GET @Path("{id}") @Produces("application/xml") public Machine show(@PathParam("id") Long id) { return new MachineRepository().retrieve(id); } @GET @Produces("application/xml") public Machines list() { Machines machines = new Machines(); machines.setMachine(new MachineRepository().list()); return machines;
    73. 73. @Path("/machines")public class MachineResource { MachineResource @Path("{id}/softwares") public SoftwareResource softwares(@PathParam("id") Long id) { Machine machine = new MachineRepository().retrieve(id); if (machine != null) { SoftwareResource softwareResource = new SoftwareResource(); softwareResource.setMachine(machine); return softwareResource; } throw new WebApplicationException(404); } @POST @Consumes("application/xml") public Response create(Machine machine) { Long id = new MachineRepository().save(machine); return Response.created(UriBuilder.fromPath("/" + id).build()).build(); } @GET @Path("{id}") @Produces("application/xml") public Machine show(@PathParam("id") Long id) { return new MachineRepository().retrieve(id); } @GET @Produces("application/xml") public Machines list() { Machines machines = new Machines(); machines.setMachine(new MachineRepository().list()); return machines;
    74. 74. Server Modelaccording toLeonard Richardson, 2008 one uri + one verb
    75. 75. Server Modelaccording toLeonard Richardson, 2008 Ugly one uri + one verb
    76. 76. Server Modelaccording toLeonard Richardson, 2008 multiple uris one uri + one verb
    77. 77. Server Modelaccording toLeonard Richardson, 2008 Less ugly multiple uris one uri + one verb
    78. 78. Server Modelaccording toLeonard Richardson, 2008 multiple verbs multiple uris one uri + one verb
    79. 79. Server Modelaccording toLeonard Richardson, 2008 cute CRUD multiple verbs multiple uris one uri + one verb
    80. 80. Server Model restaccording toLeonard Richardson, 2008 hypermedia multiple verbs multiple uris one uri + one verb
    81. 81. JAX-RS 2 wishlist
    82. 82. guj
    83. 83. vraptor 0, 2, 3
    84. 84. java keynotes
    85. 85. exemplosvraptor.org
    86. 86. 1. repetições?@Path("/services")public class Services {  @GET  public Response services(    @QueryParam("action") String action) {    ServiceFactory factory = new ServiceFactory();    Service service = factory.getServiceFor(action);    return service.execute();  }}
    87. 87. você quer fazer copy+paste?
    88. 88. repetir = inferno
    89. 89. Convention over Configuration@Resourcepublic class Services { private final ServiceFactory factory; public Services(ServiceFactory factory) { this.factory = factory; } public void services(String action) { factory.getServiceFor(action).execute(); }}
    90. 90. yes!
    91. 91. yes!
    92. 92. 2. TDD: hard to test@Path("/products")public class Products { coupled to the  @GET implementation  public Response create(    @QueryParam("what") String what) {    // persists    return Response.ok()             .type("application/xml")             .entity("<product>...</product>")             .build();  }}
    93. 93. 3. Content negotiation na unha@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  public Response install(Software software) {    software = SoftwareRepository.register(software);    long id = software.getId();    URI uri = UriBuilder.fromPath("/softwares/" + id)                                       .build(); software.install();    return Response.created(uri).build();  }
    94. 94. 4. URI coupling writing the URI once...  @GET @Path("/softwares/{id}")  public Response install(@QueryParam("id") Softwaresoftware) { // ...  }
    95. 95. 4. URI coupling writing the URI again several times@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  public Response install(Software software) {    software = SoftwareRepository.register(software);    long id = software.getId();    URI uri = UriBuilder.fromPath("/softwares/" + id)                                       .build(); software.install();    return Response.created(uri).build();  }
    96. 96. 4. URI coupling code@Path("/softwares")public class SoftwareResource {  @POST @Consumes("application/xml")  public Response install(Software software) {    software = SoftwareRepository.register(software);    long id = software.getId();    URI uri = UriBuilder.fromPath("/softwares/" + id)                                       .build(); software.install();    return Response.created(uri).build();  }
    97. 97. repetir = inferno
    98. 98. 5. Parameter list@Path("/machines")public class MachineResource { @Path("{id}/softwares") public SoftwareResource softwares(@PathParam("id") Long id) { Machine machine = new MachineRepository().retrieve(id); if (machine == null) { throw new WebApplicationException(404); } // ... }}
    99. 99. repetir = inferno
    100. 100. java =string oriented?
    101. 101. java =annotation oriented?
    102. 102. Cliente REST
    103. 103. Response response = client.at ("http://localhost:9998/user/574").get(); 6. Client internal DSLs
    104. 104. Response response = client.at ("http://localhost:9998/user/574").get();User user = response.getResource();System.out.println("user: " + user.getName()); 6. Client internal DSLs
    105. 105. User user = response.getResource();System.out.println("user: " + user.getName());Link link = resource(user).getLink("machine");response = link.follow().post(new Machine());
    106. 106. Link link = resource(user).getLink("machine");response = link.follow().post(new Machine());double amount = resource(user).refresh(). getAmountDue();
    107. 107. double amount = resource(user).refresh(). getAmountDue();link = resource(user).getLink("payment");Payment payment = new Payment(amount);response = link.follow().post(payment);
    108. 108. link = resource(user).getLink("payment");Payment payment = new Payment(amount);response = link.follow().post(payment);System.out.println("payment completed");
    109. 109. link = resource(user).getLink("payment");Payment payment = new Payment(amount);response = link.follow().post(payment);System.out.println("payment completed"); uma DSL alto nívelcom suporte a hipermídia
    110. 110. bottom up design by committe mime type microformato
    111. 111. hypermídiaviabilizando a integração através da web
    112. 112. vraptor mvc based REST
    113. 113. Arun Gupta jersey vraptor 3
    114. 114. vraptor somos nozes
    115. 115. @guilhermecaelumobrigado

    ×