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