/* * <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.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.ServiceRequest; import org.young.isocket.service.ServiceResponse; import org.young.isocket.util.SocketKeys; import org.young.isocket.validatior.ObjectSizeValidator; import org.young.isocket.validatior.TransformTypeValidator; /** * <p> * 描述:数据封装Filter * </p> * * @see * @author yangjun2 * @email yangjun1120@gmail.com * */ public class ClientDataFilter extends BaseFilter { private static final Logger logger = LoggerFactory.getLogger(ClientDataFilter.class); private static ParserFactory parserFactory = new ParserFactory(); /** * struct { * service id 10byte * transaction id 32 byte * tranform type 1 byte * body lenth 10 byte * content previous length decided * } */ public static final int WRITE_HEADER_SIZE = SocketKeys.SIZE_SERVICE_ID + SocketKeys.SIZE_TRANSACTION_ID + SocketKeys.SIZE_TRANSFORM_TYPE + SocketKeys.SIZE_CONTENT + SocketKeys.SIZE_SESSION_ID;// service id + trans id +transform type +body length + content /** * 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 READ_HEADER_SIZE = SocketKeys.SIZE_SERVICE_ID + SocketKeys.SIZE_TRANSACTION_ID + SocketKeys.SIZE_TRANSFORM_TYPE + SocketKeys.SIZE_RESPONSE_CODE + SocketKeys.SIZE_CONTENT + SocketKeys.SIZE_SESSION_ID;//trans id+transform type+result code+result length + content /** * Method is called, when we write a data to the Connection. * * We override this method to perform iSocket Message -> Buffer transformation. *sslFilter * @param ctx Context of {@link FilterChainContext} processing * @return the next action * @throws java.io.IOException */ @Override public NextAction handleWrite(final FilterChainContext ctx) throws IOException { if (logger.isDebugEnabled()) { logger.debug("context:{} handleWrite.", ctx); } ServiceRequest message = ctx.getMessage(); Assert.notNull(message, "service request must not be null!"); Assert.isTrue((message.getId() != null) && (message.getId().length() == 32), "length of Id must be 32 byte"); Assert.isTrue((message.getServiceId() != null) && (message.getServiceId().length() == 10), "length of ServiceId must be 10 byte"); ITextProtocolParser parser = parserFactory.getParser(message.getTransformType()); Assert.notNull(parser, "parser must not be null"); String serializeStr = message.getRequestObject() == null ? "" : parser.to(message.getRequestObject()); ObjectSizeValidator.validate(serializeStr); final int size = WRITE_HEADER_SIZE + serializeStr.length(); // 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: sid 10 output.put(message.getServiceId().getBytes(SocketKeys.CHARSET_NAME_UTF8)); //header: id 32 output.put(message.getId().getBytes(SocketKeys.CHARSET_NAME_UTF8)); //header: transform type 1 output.put(("" + message.getTransformType()).getBytes(SocketKeys.CHARSET_NAME_UTF8)); //header: result message length output.put(StringUtils.leftPad("" + serializeStr.length(), 10, "0").getBytes(SocketKeys.CHARSET_NAME_UTF8)); String sessionId = StringUtils.leftPad( StringUtils.isEmpty(message.getSessionId()) ? "" : message.getSessionId(), SocketKeys.SIZE_SESSION_ID, " "); output.put(sessionId.getBytes(SocketKeys.CHARSET_NAME_UTF8)); // Body 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 { if (logger.isDebugEnabled()) { logger.debug("context:{} handleRead.", ctx); } // Get the source buffer from the context final Buffer sourceBuffer = ctx.getMessage(); 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); } //check //transform type byte[] types = new byte[SocketKeys.SIZE_TRANSFORM_TYPE]; types[0] = sourceBuffer.get(SocketKeys.SIZE_SERVICE_ID + SocketKeys.SIZE_TRANSACTION_ID);//SIZE_CONTENT 后取1byte // Construct a message final ServiceResponse tmpMsg = new ServiceResponse(); String transformType = new String(types, SocketKeys.CHARSET_NAME_UTF8); TransformTypeValidator.validate(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 + SocketKeys.SIZE_RESPONSE_CODE); } //final int bodyLength = sourceBuffer.getInt(READ_HEADER_SIZE - 10); final int resultLength = Integer.parseInt(new String(lens, SocketKeys.CHARSET_NAME_UTF8)); // The complete message length final int completeMessageLength = READ_HEADER_SIZE + resultLength; // 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); } // 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; // Construct a message final ServiceResponse message = new ServiceResponse(); byte[] sids = new byte[SocketKeys.SIZE_SERVICE_ID]; sourceBuffer.get(sids); message.setServiceId(new String(sids, SocketKeys.CHARSET_NAME_UTF8)); //set id byte[] ids = new byte[SocketKeys.SIZE_TRANSACTION_ID]; sourceBuffer.get(ids); message.setId(new String(ids, SocketKeys.CHARSET_NAME_UTF8)); //set transfrom type types = new byte[SocketKeys.SIZE_TRANSFORM_TYPE]; sourceBuffer.get(types); message.setTransformType(new String(types, SocketKeys.CHARSET_NAME_UTF8)); // result code final byte[] rescodes = new byte[SocketKeys.SIZE_RESPONSE_CODE]; sourceBuffer.get(rescodes); message.setResponseCode(Integer.parseInt(new String(rescodes, SocketKeys.CHARSET_NAME_UTF8))); //body length lens = new byte[SocketKeys.SIZE_CONTENT]; sourceBuffer.get(lens); //session id 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); // result body if (resultLength > 0) { final byte[] results = new byte[resultLength]; sourceBuffer.get(results); String rstMsg = new String(results, SocketKeys.CHARSET_NAME_UTF8); //success if (message.getResponseCode() == 0) { ITextProtocolParser parser = parserFactory.getParser(message.getTransformType()); Assert.notNull(parser, "parser must not be null"); Object responseObject = parser.from(rstMsg); message.setResponseObject(responseObject); } else {//error //set response message message.setResponseMessage(rstMsg); } } 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); } }