Poche chiacchiere e tanto codice per cercare rendere la nostra vita di
sviluppatori più divertente.
Parleremo di JAX-RS, le annotazioni, l'MVC che mette a disposizione e
l'integrazione di Jersey con Guice.
Useremo AOP per gestire log, transazioni e con l'aiuto di Infinispan
limiteremo le chamate concorrenti sul nostro cluster.
2. Chi sono?
@PerRequest
public class Presentation {
@GET
@Path("/JugMilano/People/DomenicoBriganti")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserDetails() {
UserDetails userdet = new UserDetails();
userdet.setCompany("Eidon srl");
userdet.setEmail("dometec@gmail.com");
userdet.setLinkedin("http://www.linkedin.com/in/dometec");
userdet.setBlog("http://tipsaboutmywork.blogspot.com/");
...
return Response.ok(userdet).build();
}
JUG Milano – Meeting #48 2
3. Agenda
JAX-RS
Jersey con Guice/AOP
Demos:
Log delle richieste
Trim dei parametri Stringa in ingresso
Evitare chiamate identiche su un cluster
Transazioni
Login cookie
JUG Milano – Meeting #48 3
4. JAX-RS
Java API for RESTful Web Services
Release 1.1, JSR 311, 2009, JEE6 Full
Release futura 2.0, JSR 339, (EDR2 2012), JEE7
Package: javax.ws.rest
Implementazioni: Jersey (RI), Apache CXF,
RESTEasy, Apache Wink
JUG Milano – Meeting #48 4
5. Jersey
Open source, RI for JAX-RS
Jersey 1.x (1.13b1) implements JAX-RS 1.1
Jersey 2.x (mileston 3) implements JAX-RS 2
CDDL + GPL 1.1
JUG Milano – Meeting #48 5
6. Jersey Hello World
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
@Path("/hello")
public class HelloWorldService {
@GET
@Path("/{param}")
public Response getMsg(@PathParam("param") String msg) {
String output = "Echo: " + msg;
return Response.ok(output).build();
}
}
JUG Milano – Meeting #48 6
12. Demo 1 – Log delle richieste (interc.)
public class LogCall implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Logger logger = LoggerFactory.getLogger(invocation.getThis().getClass());
String arg = Joiner.on(", ").useForNull("null").join(invocation.getArguments());
logger.debug("{} ({}).", invocation.getMethod().getName(), arg);
Object result = invocation.proceed();
logger.trace("Output: {}.", result);
return result;
}
}
JUG Milano – Meeting #48 12
13. Demo 1 – Log delle richieste (bind)
public class GuiceConfig extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
@Override
protected void configureServlets() {
Map<String, String> params = new HashMap<String, String>();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example.demo");
...
filter("/*").through(GuiceContainer.class, params);
install(new Module() {
public void configure(Binder binder) {
LogCall logCall = new LogCall();
TrimAndNullInterceptor trimAndNullableInterceptor = new TrimAndNullInterceptor();
bindInterceptor(
Matchers.annotatedWith(Path.class),
Matchers.annotatedWith(GET.class).or(Matchers.annotatedWith(POST.class))
.or(Matchers.annotatedWith(PUT.class)).or(Matchers.annotatedWith(DELETE.class)),
trimAndNullableInterceptor, logCall);
JUG Milano – Meeting #48 13
14. Demo 2 – Trim parametri (interc.)
public class TrimAndNullInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
for (int i = 0; i < invocation.getArguments().length; i++) {
if (invocation.getArguments()[i] != null &&
invocation.getArguments()[i] instanceof String) {
String sparam = (String) invocation.getArguments()[i];
String trim = sparam.trim();
if (trim.isEmpty())
invocation.getArguments()[i] = null;
else
invocation.getArguments()[i] = trim;
}
}
return invocation.proceed();
}
}
JUG Milano – Meeting #48 14
15. Demo 2 – Trim parametri (bind)
public class GuiceConfig extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
@Override
protected void configureServlets() {
Map<String, String> params = new HashMap<String, String>();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example.demo");
...
filter("/*").through(GuiceContainer.class, params);
install(new Module() {
public void configure(Binder binder) {
LogCall logCall = new LogCall();
TrimAndNullInterceptor trimAndNullableInterceptor = new TrimAndNullInterceptor();
bindInterceptor(
Matchers.annotatedWith(Path.class),
Matchers.annotatedWith(GET.class).or(Matchers.annotatedWith(POST.class))
.or(Matchers.annotatedWith(PUT.class)).or(Matchers.annotatedWith(DELETE.class)),
trimAndNullableInterceptor, logCall);
JUG Milano – Meeting #48 15
16. Evitare richieste duplicate
Problemi:
Ristrasmissioni
Doppi submit (anche Tripli...) da browser
Timeout lato client che scatena altre prove di richieste
Rimedi:
Hashtable con chiamate attualmente in corso
429 Too Many Requests (RFC 6585)
Infinispan con lock condiviso per sistemi cluster
JUG Milano – Meeting #48 16
17. Demo 3 – Richieste duplicate (uso)
@POST
@UniqueCallOnCluster
public Response getAccountBalance(@FormParam("fromuser")...
@POST
@UniqueCallOnCluster
public Response getAccountBalance(@KeyParameter
@FormParam("fromuser")...
JUG Milano – Meeting #48 17
18. Demo 3 – Richieste duplicate (Inter.)
public class UniqueCallOnClusterInterceptor implements MethodInterceptor {
...
public Object invoke(MethodInvocation invocation) throws Throwable {
String classname = invocation.getMethod().getDeclaringClass().getSimpleName();
String methodName = invocation.getMethod().getName();
String key = classname + "_" + methodName + "_" + extractParameterValue(invocation);
TransactionManager tm = keyCallOnClusterService.getTransactionManager();
tm.begin();
boolean success = keyCallOnClusterService.lock(key);
if (!success) {
logger.info("Non posso effettuare il lock sul cluster per la chiave {}.", key);
return Response.status(429).entity("Another call with same parameter is in progress.").build();
}
String runningServer = (String) keyCallOnClusterService.get(key);
if (runningServer != null) {
logger.info("Chiamata già in corso, server {}.", runningServer);
return Response.status(429).entity("Another call with same parameter is in progress.").build();
}
keyCallOnClusterService.put(key, "todo-hostname");
tm.commit();
...
return invocation.proceed();
...
keyCallOnClusterService.remove(key);
}
JUG Milano – Meeting #48 18
19. Demo 3 – Richieste duplicate (bind)
public class GuiceConfig extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
@Override
protected void configureServlets() {
Map<String, String> params = new HashMap<String, String>();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example.demo");
...
filter("/*").through(GuiceContainer.class, params);
install(new Module() {
public void configure(Binder binder) {
UniqueCallOnClusterInterceptor uniqueCallOnClusterInterceptor =
new UniqueCallOnClusterInterceptor();
requestInjection(uniqueCallOnClusterInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UniqueCallOnCluster.class),
uniqueCallOnClusterInterceptor);
JUG Milano – Meeting #48 19
Rel 1.1 Goals: POJO-based, HTTP-centric, Format Independence, Container Independence, Inclusion in Java EE. La release 2.0 si focalizza su HATEOAS e implementazioni client, ma anche su Validation, MVC, Async, Filters/Handlers, migliorie al Content Negotiation. Attualmente Early Draft Review 2.
@FormParam is slightly special because it extracts information from a request representation that is of the MIME media type &quot;application/x-www-form-urlencoded&quot;
Listener per la configurazione dell'injector (bind,AOP). Filter per il processing delle richieste.
Il log delle richieste già lo abbiamo sul access log del nostro webserver o application server. Ma per quanto riquarda il body in POST o PUT non ci viene in aiuto. Con questo Interceptor possiamo loggare sul nostro file applicativo le richieste che arrivano con tutti i parametri in input e il THREAD che evade la chiamata
RFC6585: Additional HTTP Status Codes, April 2012, tra le altre cose: 3. 428 Precondition 4. 429 Too Many Requests 5. 431 Request Header Fields Too Large 6. 511 Network Authentication Required