jersey guice aop

Download Jersey Guice AOP

If you can't read please download the document

Upload: domenico-briganti

Post on 16-Apr-2017

1.412 views

Category:

Technology


0 download

TRANSCRIPT

News ed Attivit in Corso

Jersey + Guice + AOP

Domenico [email protected]

Chi sono?

@PerRequestpublic class Presentation {

@GET @Path("/JugMilano/People/DomenicoBriganti") @Produces(MediaType.APPLICATION_JSON) public Response getUserDetails() {

UserDetails userdet = new UserDetails(); userdet.setCompany("Eidon srl"); userdet.setEmail("[email protected]"); userdet.setLinkedin("http://www.linkedin.com/in/dometec"); userdet.setBlog("http://tipsaboutmywork.blogspot.com/"); ... return Response.ok(userdet).build();

}

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

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

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.

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

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(); }}

JAX-RS Annotation

Verbi HTTP: @GET, @POST, @PUT, @DELETE, @OPTION, @HEAD

Identificazione risorse: @Path

Input: @PathParam, @QueryParam, @MatrixParam, @HeaderParam, @CookieParam. @FormParam. @DefaultValue. MultivaluedMap

Content negotiation: @Produces, @Consume

@FormParam is slightly special because it extracts information from a request representation that is of the MIME media type "application/x-www-form-urlencoded"

JAX-RS Annotation e Facility

Ambiente: @Context (ServletConfig, ServletContext, HttpServletRequest, HttpServletResponse, SecurityContext, UriInfo, HttpHeaders)

Mapper, MessageBodyWriters, MessageBodyReaders: @Provider

ResponseBuilder e UriBuilder

DEMO 0

Esecuzione della slide di presentazione

Jersey-Guice integration

org.example.demo.GuiceConfig

GuiceFiltercom.google.inject.servlet.GuiceFilter

GuiceFilter/services/*/application.wadl/application.wadl/*

web.xml:

package org.example.demo;

public class GuiceConfig extends GuiceServletContextListener {

@Override protected Injector getInjector() {

return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() {

Map params = new HashMap();

params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example.demo"); params.put(ResourceConfig.FEATURE_TRACE, "true"); params.put(ResourceConfig.FEATURE_TRACE_PER_REQUEST, "true"); ...

web.xml

listener

Listener per la configurazione dell'injector (bind,AOP).Filter per il processing delle richieste.

Demo 1

Log delle richieste

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;

}

}

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

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 params = new HashMap();

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);

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;elseinvocation.getArguments()[i] = trim;}

}

return invocation.proceed();

}}

Demo 2 Trim parametri (bind)

public class GuiceConfig extends GuiceServletContextListener {

@Override protected Injector getInjector() {

return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() {

Map params = new HashMap();

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);

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

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

Demo 3 Richieste duplicate (uso)

@POST@UniqueCallOnClusterpublic Response getAccountBalance(@FormParam("fromuser")...

@POST@UniqueCallOnClusterpublic Response getAccountBalance(@KeyParameter @FormParam("fromuser")...

Come si usa

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);

}

Demo 3 Richieste duplicate (bind)

public class GuiceConfig extends GuiceServletContextListener {

@Override protected Injector getInjector() {

return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() {

Map params = new HashMap();

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);

Transazioni con AOP

Transazioni con AOP

Transazioni con AOP

Demo 4 Transazioni (uso)

package org.example.demo.controller;

@PerRequest@Transactional@Path("services/transactionalresource3")public class TransactionalResource3Write extends AbstractTransactionalResource {

@Injectpublic TransactionalResource3Write() {}

@POST@Produces("text/plain")public String get() {

...

Demo 4 Transazioni (uso)

package org.example.demo.controller;

@PerRequest@Transactional(TransactionType.ReadOnly)@Path("services/transactionalresource1")public class TransactionalResource1ReadOnly extends AbstractTransactionalResource {

@Injectpublic TransactionalResource1ReadOnly() {}

@GET@Produces("text/plain")@SuppressWarnings("unchecked")public String get() {

List list = getSession().createCriteria(DatabaseLog.class).list();

if (list.size() == 0)return "No record!";

Demo 4 Transazioni (uso)

package org.example.demo.controller;

@PerRequest@Transactional(TransactionType.RequiredNew)@Path("services/transactionalresource5")public class TransactionalResource5ReqNew extends AbstractTransactionalResource {

@Injectpublic TransactionalResource5ReqNew() {}

...

Demo 4 Transazioni (uso)

public abstract class AbstractTransactionalResource {private Session session;

@NoTransactionalpublic void setSession(Session session) {this.session = session; }

@NoTransactionalpublic Session getSession() {return session; }

@Override@NoTransactionalprotected void finalize() throws Throwable {super.finalize(); }

}

Demo 4 Transazioni 1/2 (interc.)

public class TransactionInterceptor implements MethodInterceptor {

private final ThreadLocal sessionThreadLocal;

public Object invoke(MethodInvocation invocation) throws Throwable {...Transactional transactional = invocation.getMethod().getDeclaringClass().getAnnotation(Transactional.class);...Stack sessionStack = sessionThreadLocal.get();Transaction transaction = null;Session session = null;if (!sessionStack.isEmpty())session = sessionStack.peek();

if (session == null || transType.equals(TransactionType.RequiredNew)) {

boolean readonly = false;readonly = transType.equals(TransactionType.ReadOnly);session = hibernateSessionService.openSession(readonly);transaction = session.getTransaction();transaction.begin();sessionStack.push(session);}

Session oldSession = null;AbstractTransactionalResource service = ((AbstractTransactionalResource) invocation.getThis());oldSession = service.getSession();service.setSession(session);

Object result = invocation.proceed();

Demo 4 Transazioni 2/2 (interc.)

try {

Object result = invocation.proceed();

if (transaction != null) {session.flush();transaction.commit();}

return result;

} catch (Exception e) {

transaction.rollback();throw e;

} finally {

if (transaction != null) {hibernateSessionService.closeSession(session);sessionStack.pop();if (sessionStack.isEmpty())sessionThreadLocal.remove();}

service.setSession(oldSession);

}

}

}

Demo 4 Transazioni (bind)

public class GuiceConfig extends GuiceServletContextListener {

@Override protected Injector getInjector() {

return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() {

Map params = new HashMap();

params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example.demo"); ...

filter("/*").through(GuiceContainer.class, params);

install(new Module() {

public void configure(Binder binder) {

TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); requestInjection(transactionInterceptor); bindInterceptor(Matchers.annotatedWith(Transactional.class), Matchers.not(Matchers.annotatedWith(NoTransactional.class)),TransactionInterceptor);

Demo 5 - Login Cookie (bind)

Annotation:AuthenticatedUser

ApplicationRolesAllowed

Provider:AuthenticatedUserProvider

Exception e ExceptionMapper:NotAuthenticatedException(+Mapper)

NotAuthorizedException(+Mapper)

Cookie: LoginCookieManager

Utente: DemoPrincipal

Interceptor: ApplicationRolesAllowedInterceptor

Grazie!

Domande??

Riferimenti:http://jersey.java.net

http://code.google.com/p/google-guice/

Demo webapp: https://github.com/dometec/shadedcode/tree/master/demo-webapp

Cliccate per modificare il formato del testo della strutturaSecondo livello strutturaTerzo livello strutturaQuarto livello strutturaQuinto livello strutturaSesto livello strutturaSettimo livello strutturaOttavo livello strutturaNono livello struttura

JUG Milano Meeting #48

Cliccate per modificare il formato del testo del titolo

Title of Your Presentation

Presenter's Name

Job Title
Company Name

Email