/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.ros.internal.node.service;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.ros.exception.ServiceException;
import org.ros.internal.message.MessageBufferPool;
import org.ros.message.MessageDeserializer;
import org.ros.message.MessageFactory;
import org.ros.message.MessageSerializer;
import org.ros.node.service.ServiceResponseBuilder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
/**
* @author damonkohler@google.com (Damon Kohler)
*/
class ServiceRequestHandler<T, S> extends SimpleChannelHandler {
private final ServiceDeclaration serviceDeclaration;
private final ServiceResponseBuilder<T, S> responseBuilder;
private final MessageDeserializer<T> deserializer;
private final MessageSerializer<S> serializer;
private final MessageFactory messageFactory;
private final ExecutorService executorService;
private final MessageBufferPool messageBufferPool;
public ServiceRequestHandler(ServiceDeclaration serviceDeclaration,
ServiceResponseBuilder<T, S> responseBuilder, MessageDeserializer<T> deserializer,
MessageSerializer<S> serializer, MessageFactory messageFactory,
ExecutorService executorService) {
this.serviceDeclaration = serviceDeclaration;
this.deserializer = deserializer;
this.serializer = serializer;
this.responseBuilder = responseBuilder;
this.messageFactory = messageFactory;
this.executorService = executorService;
messageBufferPool = new MessageBufferPool();
}
private void handleRequest(ChannelBuffer requestBuffer, ChannelBuffer responseBuffer)
throws ServiceException {
T request = deserializer.deserialize(requestBuffer);
S response = messageFactory.newFromType(serviceDeclaration.getType());
responseBuilder.build(request, response);
serializer.serialize(response, responseBuffer);
}
private void handleSuccess(final ChannelHandlerContext ctx, ServiceServerResponse response,
ChannelBuffer responseBuffer) {
response.setErrorCode(1);
response.setMessageLength(responseBuffer.readableBytes());
response.setMessage(responseBuffer);
ctx.getChannel().write(response);
}
private void handleError(final ChannelHandlerContext ctx, ServiceServerResponse response,
String message) {
response.setErrorCode(0);
ByteBuffer encodedMessage = Charset.forName("US-ASCII").encode(message);
response.setMessageLength(encodedMessage.limit());
response.setMessage(ChannelBuffers.wrappedBuffer(encodedMessage));
ctx.getChannel().write(response);
}
@Override
public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) throws Exception {
// Although the ChannelHandlerContext is explicitly documented as being safe
// to keep for later use, the MessageEvent is not. So, we make a defensive
// copy of the ChannelBuffer.
final ChannelBuffer requestBuffer = ((ChannelBuffer) e.getMessage()).copy();
executorService.execute(new Runnable() {
@Override
public void run() {
ServiceServerResponse response = new ServiceServerResponse();
ChannelBuffer responseBuffer = messageBufferPool.acquire();
boolean success;
try {
handleRequest(requestBuffer, responseBuffer);
success = true;
} catch (ServiceException ex) {
handleError(ctx, response, ex.getMessage());
success = false;
}
if (success) {
handleSuccess(ctx, response, responseBuffer);
}
messageBufferPool.release(responseBuffer);
}
});
super.messageReceived(ctx, e);
}
}