org.apache.maven.surefire.booter.MasterProcessCommand Maven / Gradle / Ivy
The newest version!
package org.apache.maven.surefire.booter;
/*
* 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.
*/
import java.io.DataInputStream;
import java.io.IOException;
import static java.lang.String.format;
import static org.apache.maven.surefire.util.internal.ObjectUtils.requireNonNull;
import static org.apache.maven.surefire.util.internal.StringUtils.ISO_8859_1;
import static org.apache.maven.surefire.util.internal.StringUtils.US_ASCII;
import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
/**
* Commands which are sent from plugin to the forked jvm.
* Support and methods related to the commands.
*
* @author Tibor Digana (tibor17)
* @since 2.19
*/
public enum MasterProcessCommand
{
RUN_CLASS( 0, String.class ),
TEST_SET_FINISHED( 1, Void.class ),
SKIP_SINCE_NEXT_TEST( 2, Void.class ),
SHUTDOWN( 3, String.class ),
/** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
NOOP( 4, Void.class ),
BYE_ACK( 5, Void.class );
private final int id;
private final Class> dataType;
MasterProcessCommand( int id, Class> dataType )
{
this.id = id;
this.dataType = requireNonNull( dataType, "dataType cannot be null" );
}
public int getId()
{
return id;
}
public Class> getDataType()
{
return dataType;
}
public boolean hasDataType()
{
return dataType != Void.class;
}
@SuppressWarnings( "checkstyle:magicnumber" )
public byte[] encode( String data )
{
if ( !hasDataType() )
{
throw new IllegalArgumentException( "cannot use data without data type" );
}
if ( getDataType() != String.class )
{
throw new IllegalArgumentException( "Data type can be only " + String.class );
}
final byte[] dataBytes = fromDataType( data );
final int len = dataBytes.length;
final byte[] encoded = new byte[8 + len];
final int command = getId();
setCommandAndDataLength( command, len, encoded );
System.arraycopy( dataBytes, 0, encoded, 8, len );
return encoded;
}
@SuppressWarnings( "checkstyle:magicnumber" )
public byte[] encode()
{
if ( getDataType() != Void.class )
{
throw new IllegalArgumentException( "Data type can be only " + getDataType() );
}
byte[] encoded = new byte[8];
int command = getId();
setCommandAndDataLength( command, 0, encoded );
return encoded;
}
public static Command decode( DataInputStream is )
throws IOException
{
MasterProcessCommand command = resolve( is.readInt() );
if ( command == null )
{
return null;
}
else
{
int dataLength = is.readInt();
if ( dataLength > 0 )
{
byte[] buffer = new byte[ dataLength ];
is.readFully( buffer );
if ( command.getDataType() == Void.class )
{
throw new IOException( format( "Command %s unexpectedly read Void data with length %d.",
command, dataLength ) );
}
String data = command.toDataTypeAsString( buffer );
return new Command( command, data );
}
else
{
return new Command( command );
}
}
}
String toDataTypeAsString( byte... data )
{
switch ( this )
{
case RUN_CLASS:
return new String( data, ISO_8859_1 );
case SHUTDOWN:
return new String( data, US_ASCII );
default:
return null;
}
}
byte[] fromDataType( String data )
{
switch ( this )
{
case RUN_CLASS:
return encodeStringForForkCommunication( data );
case SHUTDOWN:
return data.getBytes( US_ASCII );
default:
return new byte[0];
}
}
static MasterProcessCommand resolve( int id )
{
for ( MasterProcessCommand command : values() )
{
if ( id == command.id )
{
return command;
}
}
return null;
}
@SuppressWarnings( "checkstyle:magicnumber" )
static void setCommandAndDataLength( int command, int dataLength, byte... encoded )
{
encoded[0] = (byte) ( command >>> 24 );
encoded[1] = (byte) ( command >>> 16 );
encoded[2] = (byte) ( command >>> 8 );
encoded[3] = (byte) command;
encoded[4] = (byte) ( dataLength >>> 24 );
encoded[5] = (byte) ( dataLength >>> 16 );
encoded[6] = (byte) ( dataLength >>> 8 );
encoded[7] = (byte) dataLength;
}
}