org.apache.maven.surefire.api.stream.AbstractStreamEncoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of surefire-api Show documentation
Show all versions of surefire-api Show documentation
API used in Surefire and Failsafe MOJO, Booter, Common and test framework providers.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.surefire.api.stream;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.maven.surefire.api.report.RunMode;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
import static java.lang.Math.ceil;
import static java.nio.CharBuffer.wrap;
/**
* The base class of stream encoder.
* The type of message is expressed by opcode where the opcode object is described by the generic type {@link E}.
* @param type of the message
*/
public abstract class AbstractStreamEncoder> {
private static final byte BOOLEAN_NON_NULL_OBJECT = (byte) 0xff;
private static final byte BOOLEAN_NULL_OBJECT = (byte) 0;
private static final byte[] INT_BINARY = new byte[] {0, 0, 0, 0};
private final WritableByteChannel out;
public AbstractStreamEncoder(WritableByteChannel out) {
this.out = out;
}
@Nonnull
protected abstract byte[] getEncodedMagicNumber();
@Nonnull
protected abstract byte[] enumToByteArray(E e);
@Nonnull
protected abstract byte[] getEncodedCharsetName();
@Nonnull
protected abstract Charset getCharset();
@Nonnull
protected abstract CharsetEncoder newCharsetEncoder();
protected void write(ByteBuffer frame, boolean sendImmediately) throws IOException {
if (!sendImmediately && out instanceof WritableBufferedByteChannel) {
((WritableBufferedByteChannel) out).writeBuffered(frame);
} else {
out.write(frame);
}
}
public void encodeHeader(ByteBuffer result, E operation, RunMode runMode, Long testRunId) {
encodeHeader(result, operation);
byte[] runmode = runMode == null ? new byte[0] : runMode.getRunmodeBinary();
result.put((byte) runmode.length);
result.put((byte) ':');
result.put(runmode);
result.put((byte) ':');
result.put((byte) (testRunId == null ? 0 : 1));
if (testRunId != null) {
result.putLong(testRunId);
}
result.put((byte) ':');
}
public void encodeHeader(ByteBuffer result, E operation) {
result.put((byte) ':');
result.put(getEncodedMagicNumber());
result.put((byte) ':');
byte[] opcode = enumToByteArray(operation);
result.put((byte) opcode.length);
result.put((byte) ':');
result.put(opcode);
result.put((byte) ':');
}
public void encodeCharset(ByteBuffer result) {
byte[] charsetNameBinary = getEncodedCharsetName();
result.put((byte) charsetNameBinary.length);
result.put((byte) ':');
result.put(charsetNameBinary);
result.put((byte) ':');
}
public void encodeString(CharsetEncoder encoder, ByteBuffer result, String string) {
String nonNullString = nonNull(string);
int counterPosition = ((Buffer) result).position();
result.put(INT_BINARY).put((byte) ':');
int msgStart = ((Buffer) result).position();
encoder.encode(wrap(nonNullString), result, true);
int msgEnd = ((Buffer) result).position();
int encodedMsgSize = msgEnd - msgStart;
result.putInt(counterPosition, encodedMsgSize);
((Buffer) result).position(msgEnd);
result.put((byte) ':');
}
public void encodeInteger(ByteBuffer result, Integer i) {
if (i == null) {
result.put(BOOLEAN_NULL_OBJECT);
} else {
result.put(BOOLEAN_NON_NULL_OBJECT).putInt(i);
}
result.put((byte) ':');
}
public void encode(
CharsetEncoder encoder,
ByteBuffer result,
E operation,
RunMode runMode,
Long testRunId,
String... messages) {
encodeHeader(result, operation, runMode, testRunId);
encodeStringData(result, encoder, messages);
}
public void encode(CharsetEncoder encoder, ByteBuffer result, E operation, String... messages) {
encodeHeader(result, operation);
encodeStringData(result, encoder, messages);
}
private void encodeStringData(ByteBuffer result, CharsetEncoder encoder, String... messages) {
encodeCharset(result);
for (String message : messages) {
encodeString(encoder, result, message);
}
}
public int estimateBufferLength(
int opcodeLength,
RunMode runMode,
CharsetEncoder encoder,
int integersCounter,
int longsCounter,
String... strings) {
assert !(encoder == null && strings.length != 0);
// one delimiter character ':' + + one delimiter character ':' +
// one byte + one delimiter character ':' + + one delimiter character ':'
int lengthOfMetadata = 1 + getEncodedMagicNumber().length + 1 + 1 + 1 + opcodeLength + 1;
// one byte of length + one delimiter character ':' + + one delimiter character ':'
lengthOfMetadata += 1 + 1 + (runMode == null ? 0 : runMode.getRunmodeBinary().length) + 1;
if (encoder != null) {
// one byte of length + one delimiter character ':' + + one delimiter character ':'
lengthOfMetadata += 1 + 1 + encoder.charset().name().length() + 1;
}
// one byte (0x00 if NULL) + 4 bytes for integer + one delimiter character ':'
int lengthOfData = (1 + 4 + 1) * integersCounter;
// one byte (0x00 if NULL) + 8 bytes for long + one delimiter character ':'
lengthOfData += (1 + 8 + 1) * longsCounter;
for (String string : strings) {
String s = nonNull(string);
// 4 bytes of length of the string + one delimiter character ':' + + one delimiter character ':'
lengthOfData += 4 + 1 + (int) ceil(encoder.maxBytesPerChar() * s.length()) + 1;
}
return lengthOfMetadata + lengthOfData;
}
private static String nonNull(String msg) {
return msg == null ? "\u0000" : msg;
}
}