/*
* <p>
* 版权: ©2011
* </p>
*/
package org.young.isocket.filter;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
import org.glassfish.grizzly.memory.MemoryManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.young.icore.util.Assert;
import org.young.isocket.service.ServiceFactory;
import org.young.isocket.service.ServiceRequest;
import org.young.isocket.service.ServiceResponse;
import org.young.isocket.util.SocketKeys;
import org.young.isocket.validatior.ObjectSizeValidator;
import org.young.isocket.validatior.TransactionIdValidator;
/**
* <p>
* 描述:数据封装Filter,与ServerDataFilter区别在于报文头的定义,全部用字符串解析,便于接入其他语言。
* </p>
*
* @see
* @author yangjun2
* @email yangjun1120@gmail.com
*
*/
public class ServerDataFilter extends BaseFilter {
private static final Logger logger = LoggerFactory.getLogger(ServerDataFilter.class);
private static ParserFactory parserFactory = new ParserFactory();
// private static Map<Connection, Long> remainderTimeMap = new HashMap<Connection, Long>();
/**
* struct {
* service id 10byte
* transaction id 32 byte
* tranform type 1 byte
* body lenth 10 byte
* content previous length decided
* }
*/
public static final int READ_HEADER_SIZE = ClientDataFilter.WRITE_HEADER_SIZE;
/**
* struct{
* transaction id 32 byte
* tranform type 1 byte
* result code 4 byte
* body lenth 10 byte
* content previous length decided
* }
*/
public static final int WRITE_HEADER_SIZE = ClientDataFilter.READ_HEADER_SIZE;
/**
* Method is called, when we write a data to the Connection.
*
* We override this method to perform iSocket Message -> Buffer transformation.
*
* @param ctx Context of {@link FilterChainContext} processing
* @return the next action
* @throws java.io.IOException
*/
@Override
public NextAction handleWrite(final FilterChainContext ctx) throws IOException {
final ServiceResponse message = ctx.getMessage();
Assert.notNull(message, "service response must not be null!");
TransactionIdValidator.validate(message.getId());
int size = WRITE_HEADER_SIZE;
int resSize = 0;
String serializeStr = null;
if (message.getResponseCode() == 0) {
ITextProtocolParser parser = parserFactory.getParser(message.getTransformType());
Assert.notNull(parser, "parser must not be null");
serializeStr = parser.to(message.getResponseObject());
resSize = (serializeStr != null) ? serializeStr.length() : 0;
} else {
serializeStr = message.getResponseMessage();
resSize = (message.getResponseMessage() != null ? message.getResponseMessage().length() : 0);
}
ObjectSizeValidator.validate(serializeStr);
size += resSize;
// Retrieve the memory manager
final MemoryManager memoryManager = ctx.getConnection().getTransport().getMemoryManager();
// allocate the buffer of required size
final Buffer output = memoryManager.allocate(size);
// Allow Grizzly core to dispose the buffer, once it's written
output.allowBufferDispose(true);
//header:service id
output.put(message.getServiceId().getBytes(SocketKeys.CHARSET_NAME_UTF8));
//header:transaction id
output.put(message.getId().getBytes(SocketKeys.CHARSET_NAME_UTF8));
//header:transform type
output.put(("" + message.getTransformType()).getBytes(SocketKeys.CHARSET_NAME_UTF8));
//header: result code
output.put(StringUtils.leftPad("" + message.getResponseCode(), SocketKeys.SIZE_RESPONSE_CODE, "0").getBytes(
SocketKeys.CHARSET_NAME_UTF8));
//header: result message length
output.put(StringUtils.leftPad("" + resSize, SocketKeys.SIZE_CONTENT, "0").getBytes(
SocketKeys.CHARSET_NAME_UTF8));
//header: session id;
String sessionId = StringUtils.leftPad(
StringUtils.isEmpty(message.getSessionId()) ? "" : message.getSessionId(), SocketKeys.SIZE_SESSION_ID,
" ");
output.put(sessionId.getBytes(SocketKeys.CHARSET_NAME_UTF8));
// result message
//serializeStr = (serializeStr == null) ? "" : serializeStr;
if (!StringUtils.isEmpty(serializeStr)) {
output.put(serializeStr.getBytes(SocketKeys.CHARSET_NAME_UTF8));
}
// Set the Buffer as a context message
ctx.setMessage(output.flip());
// Instruct the FilterChain to call the next filter
return ctx.getInvokeAction();
}
/**
* Method is called, when new data was read from the Connection and ready
* to be processed.
*
* We override this method to perform Buffer -> ISocket Message transformation.
*
* @param ctx Context of {@link FilterChainContext} processing
* @return the next action
* @throws java.io.IOException
*/
@Override
public NextAction handleRead(final FilterChainContext ctx) throws IOException {
Connection conn = null;
try {
// Get the source buffer from the context
final Buffer sourceBuffer = ctx.getMessage();
conn = ctx.getConnection();
final int sourceBufferLength = sourceBuffer.remaining();
// If source buffer doesn't contain header
if (sourceBufferLength < READ_HEADER_SIZE) {
// stop the filterchain processing and store sourceBuffer to be
// used next time
return ctx.getStopAction(sourceBuffer);
}
//检查,并没有移动Buffer的pos
//check service id
byte[] svcs = new byte[SocketKeys.SIZE_SERVICE_ID];
for (int i = 0; i < SocketKeys.SIZE_SERVICE_ID; i++) {
svcs[i] = sourceBuffer.get(i);
}
String serviceId = new String(svcs, SocketKeys.CHARSET_NAME_UTF8);
Assert.isTrue(ServiceFactory.getInstance().isExist(serviceId),
String.format("service id:%s isn't exists!", serviceId));
//check transform type
byte[] types = new byte[SocketKeys.SIZE_TRANSFORM_TYPE];
types[0] = sourceBuffer.get(SocketKeys.SIZE_SERVICE_ID + SocketKeys.SIZE_TRANSACTION_ID);
String transformType = new String(types, SocketKeys.CHARSET_NAME_UTF8);
Assert.isTrue(
transformType.equals(SocketKeys.TRANSFORM_JSON) || transformType.equals(SocketKeys.TRANSFORM_XML),
String.format("tranform type(%s) is valid", transformType));
// Get the body length
byte[] lens = new byte[SocketKeys.SIZE_CONTENT];
for (int i = 0; i < SocketKeys.SIZE_CONTENT; i++) {
lens[i] = sourceBuffer.get(i + SocketKeys.SIZE_SERVICE_ID + SocketKeys.SIZE_TRANSACTION_ID
+ SocketKeys.SIZE_TRANSFORM_TYPE);
}
final int bodyLength = Integer.parseInt(new String(lens, SocketKeys.CHARSET_NAME_UTF8));
// The complete message length
final int completeMessageLength = READ_HEADER_SIZE + bodyLength;
// If the source message doesn't contain entire body
if (sourceBufferLength < completeMessageLength) {
// stop the filterchain processing and store sourceBuffer to be
// used next time
return ctx.getStopAction(sourceBuffer);
}
//检查结束
//转换
//transform to ServiceRequest
final ServiceRequest message = new ServiceRequest();
//set service id 10
svcs = new byte[SocketKeys.SIZE_SERVICE_ID];
sourceBuffer.get(svcs);
message.setServiceId(new String(svcs, SocketKeys.CHARSET_NAME_UTF8));
//set id 32
final byte[] ids = new byte[SocketKeys.SIZE_TRANSACTION_ID];
sourceBuffer.get(ids);
message.setId(new String(ids, SocketKeys.CHARSET_NAME_UTF8));
//set transform type 1
types = new byte[SocketKeys.SIZE_TRANSFORM_TYPE];
sourceBuffer.get(types);
message.setTransformType(new String(types, SocketKeys.CHARSET_NAME_UTF8));
// Get the body length
lens = new byte[SocketKeys.SIZE_CONTENT];
sourceBuffer.get(lens);
final byte[] ssids = new byte[SocketKeys.SIZE_SESSION_ID];
sourceBuffer.get(ssids);
String sessionId = new String(ssids, SocketKeys.CHARSET_NAME_UTF8).trim();
message.setSessionId(StringUtils.isEmpty(sessionId) ? null : sessionId);
// Check if the source buffer has more than 1 complete message
// If yes - split up the first message and the remainder
final Buffer remainder = sourceBufferLength > completeMessageLength ? sourceBuffer
.split(completeMessageLength) : null;
// result body
if (bodyLength > 0) {
final byte[] requests = new byte[bodyLength];
sourceBuffer.get(requests);
String requestStr = new String(requests, SocketKeys.CHARSET_NAME_UTF8);
ITextProtocolParser parser = parserFactory.getParser(message.getTransformType());
Assert.notNull(parser, "parser must not be null");
Object requestObject = parser.from(requestStr);
//set result
message.setRequestObject(requestObject);
}
ctx.setMessage(message);
// We can try to dispose the buffer
sourceBuffer.tryDispose();
//转换结束
// Instruct FilterChain to store the remainder (if any) and continue execution
return ctx.getInvokeAction(remainder);
} catch (Exception e) {
if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
}
}