Spring gRPC - How intercept an exception with body info - java

I have a project using spring gRPC. I created a interceptor, when services response succsesfully, the interceptor work fine, but when the service catch an exception, the interceptor not work.
The service:
#GrpcService
public class ClientAppImpl extends ClientAppGrpc.ClientAppImplBase {
#Autowired MyService myService;
#Override
public void myMethod(Request request, StreamObserver<CreateDigitalCertificateResponse> responseObserver) {
try {
Response response = myService.doStuff(request);
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch(Exception ex) {
responseObserver.onError(Status.INTERNAL.withDescription(exception.getMessage()).withCause(exception).asRuntimeException());
}
}
}
The interceptor:
#GrpcGlobalServerInterceptor
public class GrpcServerInterceptor implements ServerInterceptor {
public static final String REQUEST_ID_HEADER = "request-id-bin";
public static final Metadata.Key<byte[]> REQUEST_ID_METADATA_KEY = Metadata.Key.of(REQUEST_ID_HEADER, Metadata.BINARY_BYTE_MARSHALLER);
private static final Map<String, GrpcCall> CALLS = new HashMap<>();
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
ForwardingServerCall<ReqT, RespT> responseServerCall = new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT response) {
String callId = new String(headers.get(REQUEST_ID_METADATA_KEY));
GrpcCall grpcCall = CALLS.get(callId);
grpcCall.setResponse(response);
GrpcCallProcessor grpcCallProcessor = new GrpcCallProcessor(grpcCall);
grpcCallProcessor.processCall();
super.sendMessage(response);
}
};
ServerCall.Listener<ReqT> listenerWithContext = Contexts.interceptCall(Context.current(), responseServerCall, headers, next);
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(listenerWithContext) {
#Override
public void onMessage(ReqT request) {
String callId = UUID.randomUUID().toString();
headers.put(REQUEST_ID_METADATA_KEY, callId.getBytes());
GrpcCall grpcCall = new GrpcCall();
grpcCall.setCall(call);
grpcCall.setHeaders(headers);
grpcCall.setRequest(request);
CALLS.put(callId, grpcCall);
super.onMessage(request);
}
};
}
}
When the service not catch an exception, the interceptor work fine, and method sendMessage is called. But when the service catch an exception, the method sendMessage is not called.
Is there any way for intercept an exception, and get de request body in the interceptor?
Thanks!

If myService.doStuff() throws an exception, there is not any message being sent. sendMessage() won't be called, but close(Status, Metadata) still will be.
The current usage of headers is broken. Metadata is not thread-safe and you do not know what the current "owner" of the object is doing with it. headers.get(REQUEST_ID_METADATA_KEY) should be performed within interceptCall() directly, before returning.
It isn't entirely clear what the purpose of onMessage() is, but it looks like maybe you should make a copy of the metadata (new Metadata().merge(headers) within interceptCall().

Related

What is the correct way to capture the Http Status code with Spring AOP?

I am creating an aspect to register my application using org.springframework.web.bind.annotation.RestController like #Pointcut, this works perfectly when my class responds normally, but when an exception occurs for some reason, the returned httpStatus is always 200, even If my http response returns 500 when an error occurs, I think this is because RestController does not set the http status, but delegates it to the exception handler, how do I fix this and still have traceability on top of the restcontroller?
Follow my rest controller
#Slf4j
#RestController
#RequestMapping("/api/conta")
public class ContaResourceHTTP {
#JetpackMethod("Pagamento de conta")
#PostMapping("/pagamento")
public void realizarPagamento(#RequestBody DTOPagamento dtoPagamento) throws InterruptedException
{
}
#JetpackMethod("TransferĂȘncia entre bancos")
#PostMapping("/ted")
public void realizarTED(#RequestBody DTOPagamento dtoPagamento) throws java.lang.Exception
{
if(true)
throw new Exception("XXX");
//log.info(dtoPagamento.toString());
}
}
my AOP implementation:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass = true)
#Slf4j
public class MetricsAspect {
//#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
#Pointcut("execution(* javax.servlet.http.HttpServlet.*(..)) *)")
public void springBeanPointcut() {
}
#Autowired
Tracer tracer;
#Around("springBeanPointcut()")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
long inicioProcesso = System.currentTimeMillis();
joinPoint.proceed();
long finalProcesso = System.currentTimeMillis();
long duracaoProcesso = finalProcesso - inicioProcesso;
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
Metrics metricas = new Metrics();
metricas.setDuracaoMs(duracaoProcesso);
metricas.setDataHoraRequisicao(milissegundosToStringDate(inicioProcesso));
metricas.setDataHoraResposta(milissegundosToStringDate(finalProcesso));
metricas.setServidorOrigem(request.getRemoteAddr());
metricas.setPortaOrigem(request.getRemotePort());
metricas.setDominioAcesso(request.getLocalName());
metricas.setPortaAcesso(request.getLocalPort());
metricas.setUrlPath(request.getRequestURI());
metricas.setMetodoHttp(request.getMethod());
metricas.setIdTransacao(tracer.currentSpan().context().traceIdString());
metricas.setIdSpan(tracer.currentSpan().context().spanIdString());
metricas.setStatusHttp(response.getStatus());
log.info(JSONConversor.toJSON(metricas));
}
public String milissegundosToStringDate(long ms) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date dataInicial = new Date(ms);
return dateFormat.format(dataInicial);
}
}
My exception handler:
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionControllerAdvice {
#ExceptionHandler({ Throwable.class })
public ResponseEntity<ApiError> handlerValidationException2(Throwable e) {
return new ResponseEntity<>(new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, e, traceRespostaAPI),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
After a while I was able to solve the problem with a solution that may not be the most elegant for the problem, basically I used two pointcuts, one in the restcontroller to intercept the #JetpackMethod annotation value and add it to the http response header with advice before and another around HttpServlet that really is the one who really gets back with the modified http status.
Here's the code below that solved my problem.
This class intercepts annotation and adds its value to the header.
#Aspect
#Component
public class InterceptRestAnnotationAspect {
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllerExecution() {}
#Before("restControllerExecution()")
public void setMetodoHttpHeader(JoinPoint joinPoint) throws Throwable {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
String origem = VerificadorOrigem.processarOrigem(joinPoint);
response.setHeader("nomeMetodo", origem);
}
}
This other class logs the servlet metrics I needed and can retrieve the value entered in the header earlier.
#Aspect
#Component
#Slf4j
public class MetricsAspect {
#Pointcut("execution(* javax.servlet.http.HttpServlet.*(..)) *)")
public void servletService() {
}
#Autowired
Tracer tracer;
#Around("servletService()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
long inicioProcesso = System.currentTimeMillis();
Object result = joinPoint.proceed();
long finalProcesso = System.currentTimeMillis();
long duracaoProcesso = finalProcesso - inicioProcesso;
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
Metrics metricas = new Metrics();
String funcionalidade = response.getHeader("nomeMetodo") == null ? "Indeterminada"
: response.getHeader("nomeMetodo");
metricas.setNivelLog("INFO");
metricas.setFuncionalidade(funcionalidade);
metricas.setDuracaoMs(duracaoProcesso);
metricas.setDataHoraRequisicao(ManipulaData.milissegundosToStringDate(inicioProcesso));
metricas.setDataHoraResposta(ManipulaData.milissegundosToStringDate(finalProcesso));
metricas.setServidorOrigem(request.getRemoteAddr());
metricas.setPortaOrigem(request.getRemotePort());
metricas.setDominioAcesso(request.getLocalName());
metricas.setPortaAcesso(request.getLocalPort());
metricas.setUrlPath(request.getRequestURI());
metricas.setMetodoHttp(request.getMethod());
metricas.setIdTransacao(tracer.currentSpan().context().traceIdString());
metricas.setIdSpan(tracer.currentSpan().context().spanIdString());
metricas.setStatusHttp(response.getStatus());
log.info(JSONConversor.toJSON(metricas));
return result;
}
}
I don't think the code after joinPoint.proceed(); gets executed in case of Exceptions.
You can have a different advice for execution in case of Exceptions:
#AfterThrowing(pointcut = "springBeanPointcut()", throwing = "e")
public void afterThrowingAdvice(JoinPoint jp, Exception e) {
....
}

Synchronize asynchronous communication

I have a REST service which receives some data and check the data through an ansynchronous IBM MQ request.
REST controller:
#RestController
#RequestMapping("/request")
public class RequestController {
#RequestMapping(method = RequestMethod.POST)
public Response postRequest(#RequestBody Request request) {
String data = request.getData();
jmsSender.send(data);
// Now I need the response from MQ
// String mqResponse = ...
if (mqIsValid(mqResponse)) {
return createValidResponse();
}
return createNotValidResponse();
}
}
MQ sender:
#Service
public class JmsSender {
public void send(String data) {
jmsTemplate.convertAndSend("QUEUE.TO.MQ", data);
}
}
MQ receiver:
#Component
public class JmsReceiver {
#JmsListener(destination = "QUEUE.FROM.MQ, containerFactory = "DefaultJmsListenerContainerFactory")
public void receiveMessage(String message) {
// How to pass the message to the controller?
}
}
How can I wait for the right data from MQ to create the correct response in the controller?
Is it possible to use a BlockingQueue for this like described here? In my case I have to distinguish the data. I can't just take the first data from the blocking queue.
If for example there are two REST requests simultaneously (with data: abc and xyz). How can I make sure to response the right answer not just the first answer I get from MQ?
I also can't change the MQ interface.
Try using a CountDownLatch like below.
#RestController
#RequestMapping("/request")
public class RequestController {
#RequestMapping(method = RequestMethod.POST)
public Response postRequest(#RequestBody Request request) {
final CountDownLatch jmsLatch = new CountDownLatch (1);
String data = request.getData();
jmsSender.send(data, jmsLatch);
try {
latch.await(); // wait untill latch counted down to 0
} catch (InterruptedException e) {
return createNotValidResponse();
}
return createValidResponse();
}
}
Modify the send method to get the CountDownLatch from controller.
#Service
public class JmsSender {
public void send(String data, final CountDownLatch jmsLatch) {
jmsLatch.await();
jmsTemplate.convertAndSend("QUEUE.TO.MQ", data);
}
}
Modify the receive method to get the same CountDownLatch from controller.
#Component
public class JmsReceiver {
#JmsListener(destination = "QUEUE.FROM.MQ", containerFactory = "DefaultJmsListenerContainerFactory")
public void receiveMessage(String message, final CountDownLatch jmsLatch) {
// Pass the message to the controller
jmsLatch.countDown();
}
}
The trick here is you have to spread the same CountDownLatch instance from controller to the sender and receiver class and call the countDown method after you receive the message.
Since I couldn't find any suitable solution for me, I've created a simple waiting mechanism to get the data.
MqReceiver:
#Component
public class JmsReceiver {
private final Lock lock;
private final Condition containsKey;
private final Map<String, String> responses;
public JmsReceiver() {
this.lock = new ReentrantLock();
this.containsKey = lock.newCondition();
this.responses = new HashMap<>();
}
#JmsListener(destination = "QUEUE.FROM.MQ", containerFactory = "DefaultJmsListenerContainerFactory")
public void receiveMessage(String message) {
put(getKeyFromMessage(message), message);
}
public String get(String key) throws InterruptedException {
lock.lock();
try {
while (!responses.containsKey(key)) {
containsKey.await();
}
return responses.get(key);
} finally {
lock.unlock();
}
}
public void put(String key, String messagee) {
lock.lock();
try {
responses.put(key, messagee);
containsKey.signalAll();
} finally {
lock.unlock();
}
}
}
This can be used in the controller:
#RestController
#RequestMapping("/request")
public class RequestController {
#RequestMapping(method = RequestMethod.POST)
public Response postRequest(#RequestBody Request request) {
String data = request.getData();
jmsSender.send(data);
String key = getKeyFromData(data);
// waits until MQ sends the data
String mqResponse = jmsReceiver.get(key);
if (mqIsValid(mqResponse)) {
return createValidResponse();
}
return createNotValidResponse();
}
}
Solution for scenario sync-async implementing request-reply pattern with jms(activemq)
The idea of this example is working in two different services in a different jvm. The solution is tested concurrent with several instances services:
Service 1 (M1) - Rest api synchronous and in some point start an
async flow using activemq to call the second Service M2 implementing Integration Pattern Request-Reply. You don't need to stop or wait any thread, the jms pattern implements the ack Session.AUTO_ACKNOWLEDGE.
#PostMapping
public AnyDto sendMessage(final AnyDto anyDto) {
return routeService.send(anyDto);
}
public void flowOrchestation (final anyDto data) throws JMSException {
final ObjectMessage objectMessage = composeTemplateMessage(data);
final AnyDto responseDto = jmsMessagingTemplate.convertSendAndReceive(new ActiveMQQueue("queue.request"),
objectMessage, AnyDto.class);
}
private ObjectMessage composeTemplateMessage(final AnyDto data) throws JMSException {
jmsTemplate.setReceiveTimeout(10000L);
jmsMessagingTemplate.setJmsTemplate(jmsTemplate);
Session session = jmsMessagingTemplate.getConnectionFactory().createConnection()
.createSession(false, Session.AUTO_ACKNOWLEDGE);
final ObjectMessage objectMessage = session.createObjectMessage(data);
objectMessage.setJMSCorrelationID(UUID.randomUUID().toString());
objectMessage.setJMSReplyTo(new ActiveMQQueue("queue.response"));
objectMessage.setJMSExpiration(0);
objectMessage.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
return objectMessage;
}
Timeout and expiration can be modified depending your requeriments. 0 expiration means no time to expire.
Service 2 (M2): Just receive the message and make the response to JmsReplyTo setted on M1.
#Component
public class Consumer implements SessionAwareMessageListener<Message> {
#Override
#JmsListener(destination = "${queue.request}")
public void onMessage(Message message, Session session) throws JMSException {
AnyDto anyDto = (AnyDto) ((ActiveMQObjectMessage) message).getObject();
//do some stuff
final ObjectMessage responseMessage = new ActiveMQObjectMessage();
responseMessage.setJMSCorrelationID(message.getJMSCorrelationID());
responseMessage.setObject(dtoModified);
final MessageProducer producer = session.createProducer(message.getJMSReplyTo());
producer.send(responseMessage);
}}

Jersey async request handling with programmatical resource registration

We used
org.glassfish.jersey.server.model.ResourceMethod$Builder
to register the method and the handler.
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod);
methodBuilder.produces(restContext.getProduceContent()).handledBy(inflector);
methodBuilder.consumes(restContext.getConsumeContent()).handledBy(inflector);
The handler class implements the org.glassfish.jersey.process.Inflector<ContainerRequestContext, Response>
public class CommonMethodInflector implements Inflector<ContainerRequestContext, Response>
{
#Override
public Response apply(ContainerRequestContext request)
{
//sync bloc
//using resqest object we do processing in different maner
incRestFeRequestCounters(request.getMethod());
Response response = processIncoming(request);`enter code here`
}
}
Could you please help us in creating the async handler.
our requirement in short:
At runtime only we know the http method and other resources to register.
So, we can not use annotations for resource & httpMethod registration.
We need only programmatic resource registration.
In handler We need the request object so that we can access what method and what json body in it.
we need to make the async response as we are doing huge operation in the processing request.
The first thing you need to do is to suspend the resource method:
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod)
.suspend(AsyncResponse.NO_TIMEOUT, TimeUnit.Seconds);
Then you have few choices how to process the request in async mode:
"Arbitrary" handler class
builder.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(MyHandler.class, MyHandler.class.getMethod("handle", AsyncResponse.class));
and
public class MyHandler {
public void handle(final #Suspended AsyncResponse response) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
}
}
Inflector class
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
// Can be registered only as a class otherwise the
// #Context injection would be out of request scope.
.handledBy(MyAsyncInflector.class);
and
public class MyAsyncInflector implements Inflector<ContainerRequestContext, Response> {
#Context
private AsyncResponse response;
#Override
public Response apply(final ContainerRequestContext context) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
}
Annonymous Inflector
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(new Inflector<ContainerRequestContext, Response>() {
#Inject
private javax.inject.Provider<AsyncResponse> responseProvider;
#Override
public Response apply(final ContainerRequestContext context) {
// Make sure we're in request scope.
final AsyncResponse response = responseProvider.get();
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
});

Restlet Client :: how to add filters?

I suffering of a lack of documentation on the use of Restlet at the client side.
I am getting a resource on server via a ClientResource:
new ClientResource(url).get();
But the server can return an ETag header. To handle this I want to save the ETag when returned and send it back to the server when using the same url.
Currently I am doing it like this:
ClientResource clientResource = new ClientResource(url);
addEtag(url, clientResource); // add the cached ETag to the query if any
clientResource.get();
saveEtag(url, clientResource); // cache the ETag if any
I would like to do this using the Restlet framework. I am searching for days wihtout understanding the missing link.
I can extend an application, overwrite the createOutboundRoot() method and return a filter:
public class RestLetClient extends Application {
private Client client;
// instantiation of the client and other things here
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client){
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
return super.afterHandle(request, response);
}
};
}
}
BUT how can I use this filtering around the Restlet client from my business code?
EDIT
The best I could get to work until now is this:
Request request = new Request(Method.GET, uri);
//the filter created in original post
filter.handle(request).getEntity();
This works but it is not integrated in the framework. What I am achieving to do is at the client side what is only documented for the server side. On the server you would do:
public class ServerApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(GET_URL, GetResource.class);
return router;
}
}
and then start the server. The application will the be triggered on the reception of a GET request on the url.
What is the equivalent on the client side? How can I trigger a Client Application? If I have an Application running at the client side I can nicely add filters where they belong in a REST application
EDIT 2
When trying to run my client within an Application I get the error: The filter org.restlet.engine.application.RangeFilter#f372a7a was executed without a next Restlet attached to it.
Here is how I am getting the error. I have a class extending Application that is called from a JUnit test:
public class RestLetClient extends Application {
private final Client client;
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClient() {
this.client = new Client(Protocol.HTTP);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client) {
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
};
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
and the unit with a single test method:
public class RestLetClientTest {
public static final String URL = "http://localhost:8123/resource";
private RestLetClient instance;
private Server server;
#Before
public void setUp() throws Exception {
server = new Server(Protocol.HTTP, 8123, new TestApplication());
server.start();
instance = new RestLetClient();
instance.start();
}
#After
public void tearDown() throws Exception {
instance.stop();
}
#Test
public void testGet() throws Exception {
Representation representation = instance.get(URL);
System.out.println(representation.getText());
}
private class TestApplication extends Application {
#Override
public Restlet createInboundRoot() {
return new Router().attach(RestLetClientTest.URL, GetResource.class);
}
}
private class GetResource extends ServerResource {
#Get
public Representation getResource() {
return new StringRepresentation("hello world");
}
}
}
What am I doing wrong?
I had a much nicer answer from a colleague. I post it here for the documentation.
The solution is to use a ClientResource, a Filter and a Client.
The Filter becomes the next() of the ClientResource and the Client the next() of the Filter.
public class ETagFilter extends Filter {
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
super.afterHandle(request, response);
}
}
public class RestLetClient extends Application {
public Representation get(final String uri) throws Exception {
Client client = new Client(Protocol.HTTP);
ETagFilter eTagFilter = new ETagFilter();
clientResource = new ClientResource(uri);
clientResource.setNext(eTagFilter);
eTagFilter.setNext(client);
return clientResource.get(halMediaType);
}
}
For info. In my OP I was trying to transform code meant for server side into client side. That approach was wrong. My colleague pointed that the approach is much more like the use Apache HttpClient for similar needs
To have a client working you need to take the Application out of the picture since it is Server oriented according to the javadoc.
What you need is a Component, a ClientRouter and a custom ClientRoute.
Component manage connectors. A Restlet Client is a Connector.
ClientRouter dispatches to client connectors.
ClientRoute extends Filter allowing to add filters around your client handeling.
My solution:
The Component
public class RestLetComponent extends Component {
public RestLetComponent(Client client) {
getClients().add(client);
}
}
The ClientRouter
public class RestLetClientRouter extends ClientRouter {
public RestLetClientRouter(final Client client) {
super(new RestLetComponent(client));
ClientRoute clientRoute = new RestLetClientRoute(this, client);
//forcing to use only our custom route
getRoutes().clear();
getRoutes().add(clientRoute);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
}
And the custom ClientRoute that will add the filters
public class RestLetClientRoute extends ClientRoute {
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClientRoute(Router router, Client client) {
super(router, client);
}
//the filters
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected int doHandle(Request request, Response response) {
logger.debug("handling request: " + request.getMethod() + " - " + request.getResourceRef());
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
And last but not least, I apologize to the Restlet authors, the documentation is there. I was reading the Restlet in Action book but the answer is in the very well documented javadoc.

CometD publish a message back to a client

I am having a problem in sending back a message to a client. Below is my code
JavaScript
dojox.cometd.publish('/service/getservice', {
userid : _USERID,
});
dojox.cometd.subscribe('/service/getservice', function(
message) {
alert("abc");
alert(message.data.test);
});
Configuration Servlet
bayeux.createIfAbsent("/service/getservice", new ConfigurableServerChannel.Initializer() {
#Override
public void configureChannel(ConfigurableServerChannel channel) {
channel.setPersistent(true);
GetListener channelListner = new GetListener();
channel.addListener(channelListner);
}
});
GetListener class
public class GetListener implements MessageListener {
public boolean onMessage(ServerSession ss, ServerChannel sc) {
SomeClassFunction fun = new SomeClassFunction;
}
}
SomeClassFunction
class SomeClassFunction(){
}
here i am creating a boolean variable
boolean success;
if it is true send a message to client which is in javascript. how to send a message back to client. i have tried this line also.
remote.deliver(getServerSession(), "/service/getservice",
message, null);
but it is giving me an error on remote object and getServerSession method.
In order to reach your goal, you don't need to implement listeners nor to configure channels. You may need to add some configuration at a later stage, for example in order to add authorizers.
This is the code for the ConfigurationServlet, taken from this link:
public class ConfigurationServlet extends GenericServlet
{
public void init() throws ServletException
{
// Grab the Bayeux object
BayeuxServer bayeux = (BayeuxServer)getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
new EchoService(bayeux);
// Create other services here
// This is also the place where you can configure the Bayeux object
// by adding extensions or specifying a SecurityPolicy
}
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
{
throw new ServletException();
}
}
This is the code for EchoService class, taken fro this link:
public class EchoService extends AbstractService
{
public EchoService(BayeuxServer bayeuxServer)
{
super(bayeuxServer, "echo");
addService("/echo", "processEcho");
}
public void processEcho(ServerSession remote, Map<String, Object> data)
{
// if you want to echo the message to the client that sent the message
remote.deliver(getServerSession(), "/echo", data, null);
// if you want to send the message to all the subscribers of the "/myChannel" channel
getBayeux().createIfAbsent("/myChannel");
getBayeux().getChannel("/myChannel").publish(getServerSession(), data, null);
}
}

Categories

Resources