/*
* Copyright 2014-2016 the original author or authors.
*
* 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.springframework.integration.syslog.inbound;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.springframework.core.serializer.Deserializer;
import org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer;
import org.springframework.integration.ip.tcp.serializer.SoftEndOfStreamException;
import org.springframework.integration.syslog.RFC5424SyslogParser;
import org.springframework.util.Assert;
/**
* RFC5424/6587 Deserializer. Implemented as a {@link Deserializer} instead of a
* transformer because we may receive a mixture of octet counting and non-transparent
* framing - see RFC 6587.
*
* @author Duncan McIntyre
* @author Gary Russell
* @since 4.1.1
*
*/
public class RFC6587SyslogDeserializer implements Deserializer<Map<String, ?>> {
private final Deserializer<byte[]> delimitedDeserializer;
private RFC5424SyslogParser parser = new RFC5424SyslogParser();
/**
* Construct an instance using a {@link ByteArrayLfSerializer} for
* non-transparent frames.
*/
public RFC6587SyslogDeserializer() {
this.delimitedDeserializer = new ByteArrayLfSerializer();
}
/**
* Construct an instance using the specified {@link Deserializer} for
* non-transparent frames.
* @param delimitedDeserializer the Deserializer.
*/
public RFC6587SyslogDeserializer(Deserializer<byte[]> delimitedDeserializer) {
this.delimitedDeserializer = delimitedDeserializer;
}
/**
* @param parser the parser to set
*/
public void setParser(RFC5424SyslogParser parser) {
this.parser = parser;
}
@Override
public Map<String, ?> deserialize(InputStream inputStream) throws IOException {
DataInputStream stream = new DataInputStream(inputStream);
String line;
int octetCount = 0;
boolean shortRead = false;
int peek = stream.read();
if (isDigit(peek)) {
octetCount = calculateLength(stream, peek);
Assert.state(octetCount > 0, "Expected length > 0");
byte[] bytes = new byte[octetCount];
try {
stream.readFully(bytes);
}
catch (EOFException e) {
shortRead = true;
}
line = new String(bytes, getCharset());
}
else if (peek == '<') {
byte[] bytes = this.delimitedDeserializer.deserialize(inputStream);
line = "<" + new String(bytes, getCharset());
}
else if (peek < 0) {
throw new SoftEndOfStreamException();
}
else {
throw new IllegalStateException("Expected a digit or '<', got 0x" + Integer.toHexString(peek));
}
return this.parser.parse(line, octetCount, shortRead);
}
private boolean isDigit(int peek) {
return peek >= 0x30 && peek <= 0x39;
}
private int calculateLength(DataInputStream stream, int peek) throws IOException {
int length = peek & 0xf;
int c;
while (isDigit((c = stream.read()))) {
length = length * 10 + (c & 0xf);
}
return length;
}
protected String getCharset() {
return "UTF-8";
}
}