View Javadoc
1   package org.apache.maven.surefire.booter;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * Commands which are sent from plugin to the forked jvm.
31   * Support and methods related to the commands.
32   *
33   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
34   * @since 2.19
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      /** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
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 }