package de.jpaw.bonaparte.jaxrs; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.ext.MessageBodyReader; import de.jpaw.bonaparte.core.BonaPortable; import de.jpaw.bonaparte.core.MessageParser; import de.jpaw.bonaparte.core.MessageParserException; import de.jpaw.util.ByteArray; import de.jpaw.util.ByteBuilder; public abstract class AbstractBonaparteConverters<E extends Exception> extends AbstractBonaparteConverter<BonaPortable, E> implements MessageBodyReader<BonaPortable> { public static int MAXIMUM_MESSAGE_LENGTH = -1; // tunable constant (-1 = disabled) public static int READ_CHUNK_SIZE = 4000; // tunable constant public AbstractBonaparteConverters(String supportedMimeType) { super(supportedMimeType, BonaPortable.class); } protected abstract MessageParser<MessageParserException> newParser(byte [] buffer, int offset, int len); @Override public boolean isReadable(Class<?> cls, Type type, Annotation[] anno, MediaType mediaType) { LOGGER.trace("Check for reading class {} as {}", cls.getCanonicalName(), mediaType.toString()); return supportedMimeType.equals(mediaType.toString()) && supportedClass.isAssignableFrom(cls); } @Override public BonaPortable readFrom(Class<BonaPortable> cls, Type type, Annotation[] anno, MediaType mediaType, MultivaluedMap<String, String> args, InputStream is) throws IOException, WebApplicationException { // we may need to compose the full message from several parts ByteBuilder buffer = new ByteBuilder(0, ByteArray.CHARSET_UTF8); final byte [] tmp = new byte [READ_CHUNK_SIZE]; // do a loop here, because an initial read may not return the full number of bytes for (;;) { int lastRead = is.read(tmp, 0, READ_CHUNK_SIZE); LOGGER.trace("read chunk returned {} byte", lastRead); if (lastRead < 0) { break; // EOF } if (lastRead > 0) { if (MAXIMUM_MESSAGE_LENGTH > 0) { if (buffer.length() + lastRead > MAXIMUM_MESSAGE_LENGTH) throw new WebApplicationException("message length exceeds maximum allowed size of " + MAXIMUM_MESSAGE_LENGTH + " byte", Response.Status.BAD_REQUEST); } buffer.write(tmp, 0, lastRead); if (tmp[lastRead - 1] == '\n') { LOGGER.trace("got NL as last character"); break; } } } int len = buffer.length(); LOGGER.debug("received message of {} bytes, terminator{} found", len, buffer.getCurrentBuffer()[len-1] == '\n' ? "" : " NOT"); MessageParser<MessageParserException> bap = newParser(buffer.getCurrentBuffer(), 0, len); try { BonaPortable result = bap.readRecord(); LOGGER.debug("Parsed object of type {}" + result.ret$PQON()); return result; } catch (MessageParserException e) { // provide the parser message via http response LOGGER.debug("Parsing resulted in error {}", e.getMessage()); throw new WebApplicationException(e.getMessage(), Response.Status.BAD_REQUEST); } } }