1 package org.apache.maven.surefire.booter;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.DataInputStream;
23 import java.io.IOException;
24
25 import static java.nio.charset.StandardCharsets.US_ASCII;
26 import static java.util.Objects.requireNonNull;
27 import static java.lang.String.format;
28
29
30
31
32
33
34
35
36 public enum MasterProcessCommand
37 {
38 RUN_CLASS( 0, String.class ),
39 TEST_SET_FINISHED( 1, Void.class ),
40 SKIP_SINCE_NEXT_TEST( 2, Void.class ),
41 SHUTDOWN( 3, String.class ),
42
43
44 NOOP( 4, Void.class ),
45 BYE_ACK( 5, Void.class );
46
47 private final int id;
48
49 private final Class<?> dataType;
50
51 MasterProcessCommand( int id, Class<?> dataType )
52 {
53 this.id = id;
54 this.dataType = requireNonNull( dataType, "dataType cannot be null" );
55 }
56
57 public int getId()
58 {
59 return id;
60 }
61
62 public Class<?> getDataType()
63 {
64 return dataType;
65 }
66
67 public boolean hasDataType()
68 {
69 return dataType != Void.class;
70 }
71
72 @SuppressWarnings( "checkstyle:magicnumber" )
73 public byte[] encode( String data )
74 {
75 if ( !hasDataType() )
76 {
77 throw new IllegalArgumentException( "cannot use data without data type" );
78 }
79
80 if ( getDataType() != String.class )
81 {
82 throw new IllegalArgumentException( "Data type can be only " + String.class );
83 }
84
85 final byte[] dataBytes = fromDataType( data );
86 final int len = dataBytes.length;
87
88 final byte[] encoded = new byte[8 + len];
89
90 final int command = getId();
91 setCommandAndDataLength( command, len, encoded );
92 System.arraycopy( dataBytes, 0, encoded, 8, len );
93
94 return encoded;
95 }
96
97 @SuppressWarnings( "checkstyle:magicnumber" )
98 public byte[] encode()
99 {
100 if ( getDataType() != Void.class )
101 {
102 throw new IllegalArgumentException( "Data type can be only " + getDataType() );
103 }
104 byte[] encoded = new byte[8];
105 int command = getId();
106 setCommandAndDataLength( command, 0, encoded );
107 return encoded;
108 }
109
110 public static Command decode( DataInputStream is )
111 throws IOException
112 {
113 MasterProcessCommand command = resolve( is.readInt() );
114 if ( command == null )
115 {
116 return null;
117 }
118 else
119 {
120 int dataLength = is.readInt();
121 if ( dataLength > 0 )
122 {
123 byte[] buffer = new byte[ dataLength ];
124 is.readFully( buffer );
125
126 if ( command.getDataType() == Void.class )
127 {
128 throw new IOException( format( "Command %s unexpectedly read Void data with length %d.",
129 command, dataLength ) );
130 }
131
132 String data = command.toDataTypeAsString( buffer );
133 return new Command( command, data );
134 }
135 else
136 {
137 return new Command( command );
138 }
139 }
140 }
141
142 String toDataTypeAsString( byte... data )
143 {
144 switch ( this )
145 {
146 case RUN_CLASS:
147 case SHUTDOWN:
148 return new String( data, US_ASCII );
149 default:
150 return null;
151 }
152 }
153
154 byte[] fromDataType( String data )
155 {
156 switch ( this )
157 {
158 case RUN_CLASS:
159 case SHUTDOWN:
160 return data.getBytes( US_ASCII );
161 default:
162 return new byte[0];
163 }
164 }
165
166 static MasterProcessCommand resolve( int id )
167 {
168 for ( MasterProcessCommand command : values() )
169 {
170 if ( id == command.id )
171 {
172 return command;
173 }
174 }
175 return null;
176 }
177
178 @SuppressWarnings( "checkstyle:magicnumber" )
179 static void setCommandAndDataLength( int command, int dataLength, byte... encoded )
180 {
181 encoded[0] = (byte) ( command >>> 24 );
182 encoded[1] = (byte) ( command >>> 16 );
183 encoded[2] = (byte) ( command >>> 8 );
184 encoded[3] = (byte) command;
185 encoded[4] = (byte) ( dataLength >>> 24 );
186 encoded[5] = (byte) ( dataLength >>> 16 );
187 encoded[6] = (byte) ( dataLength >>> 8 );
188 encoded[7] = (byte) dataLength;
189 }
190 }