Skip to content

Commit 33474ec

Browse files
Decouple TestkitState from CommandProcessor.
This commit remoces the command processor from the TestKit state. The processor is supposed to use that state, not to be part of it. However, some requests, like `NewDriver` need the processor to trigger further state. Therefor the processor is now provided as injectable value via Jackson, so that any request can indicate that it needs a processor via a constructor argument.
1 parent c93587e commit 33474ec

File tree

11 files changed

+322
-270
lines changed

11 files changed

+322
-270
lines changed

testkit-backend/src/main/java/neo4j/org/testkit/backend/BackendServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private void handleClient( Socket clientSocket )
5050
System.out.println( "Handling connection from: " + clientSocket.getRemoteSocketAddress() );
5151
BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() ) );
5252
BufferedWriter out = new BufferedWriter( new OutputStreamWriter( clientSocket.getOutputStream() ) );
53-
CommandProcessor commandProcessor = new CommandProcessor( in, out );
53+
CommandProcessor commandProcessor = new DefaultCommandProcessor( in, out );
5454

5555
boolean cont = true;
5656
while ( cont )

testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java

Lines changed: 28 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -18,227 +18,42 @@
1818
*/
1919
package neo4j.org.testkit.backend;
2020

21-
import com.fasterxml.jackson.core.JsonProcessingException;
2221
import com.fasterxml.jackson.databind.DeserializationFeature;
22+
import com.fasterxml.jackson.databind.InjectableValues;
2323
import com.fasterxml.jackson.databind.ObjectMapper;
2424
import neo4j.org.testkit.backend.messages.TestkitModule;
25-
import neo4j.org.testkit.backend.messages.requests.TestkitRequest;
26-
import neo4j.org.testkit.backend.messages.responses.BackendError;
27-
import neo4j.org.testkit.backend.messages.responses.DriverError;
28-
import neo4j.org.testkit.backend.messages.responses.TestkitResponse;
2925

30-
import java.io.BufferedReader;
31-
import java.io.BufferedWriter;
32-
import java.io.IOException;
33-
import java.io.UncheckedIOException;
26+
import java.util.Collections;
3427

35-
import org.neo4j.driver.exceptions.Neo4jException;
36-
import org.neo4j.driver.exceptions.UntrustedServerException;
37-
import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl;
38-
39-
public class CommandProcessor
28+
public interface CommandProcessor
4029
{
41-
private final TestkitState testkitState;
42-
43-
private final ObjectMapper objectMapper = new ObjectMapper();
30+
/**
31+
* Used in ObjectMapper's injectable values.
32+
*/
33+
String COMMAND_PROCESSOR_ID = "commandProcessor";
34+
35+
/**
36+
* Reads one request and writes the response. Returns false when not able to read anymore.
37+
*
38+
* @return False when there's nothing to read anymore.
39+
*/
40+
boolean process();
41+
42+
/**
43+
* Create a new {@link ObjectMapper} configured with the appropriate testkit module and an injectable {@link CommandProcessor}.
44+
* @param processor The processor supposed to be injectable
45+
* @return A reusable object mapper instance
46+
*/
47+
static ObjectMapper newObjectMapperFor(CommandProcessor processor) {
48+
49+
final ObjectMapper objectMapper = new ObjectMapper();
4450

45-
private final BufferedReader in;
46-
private final BufferedWriter out;
47-
48-
public CommandProcessor( BufferedReader in, BufferedWriter out )
49-
{
50-
this.in = in;
51-
this.out = out;
52-
configureObjectMapper();
53-
this.testkitState = new TestkitState( this::writeResponse, this::process );
54-
}
55-
56-
private void configureObjectMapper()
57-
{
5851
TestkitModule testkitModule = new TestkitModule();
59-
this.objectMapper.registerModule( testkitModule );
60-
this.objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
61-
}
62-
63-
private String readLine()
64-
{
65-
try
66-
{
67-
return this.in.readLine();
68-
}
69-
catch ( IOException e )
70-
{
71-
throw new UncheckedIOException( e );
72-
}
73-
}
74-
75-
private void write( String s )
76-
{
77-
try
78-
{
79-
this.out.write( s );
80-
}
81-
catch ( IOException e )
82-
{
83-
throw new UncheckedIOException( e );
84-
}
85-
}
86-
87-
// Logs to frontend
88-
private void log( String s )
89-
{
90-
try
91-
{
92-
this.out.write( s + "\n" );
93-
this.out.flush();
94-
}
95-
catch ( IOException e )
96-
{
97-
}
98-
System.out.println( s );
99-
}
100-
101-
private void flush()
102-
{
103-
try
104-
{
105-
this.out.flush();
106-
}
107-
catch ( IOException e )
108-
{
109-
throw new UncheckedIOException( e );
110-
}
111-
}
112-
113-
// Reads one request and writes the response. Returns false when not able to read anymore.
114-
public boolean process()
115-
{
116-
boolean inRequest = false;
117-
StringBuilder request = new StringBuilder();
118-
119-
log( "Waiting for request" );
120-
121-
while ( true )
122-
{
123-
String currentLine = readLine();
124-
// End of stream
125-
if ( currentLine == null )
126-
{
127-
return false;
128-
}
129-
130-
if ( currentLine.equals( "#request begin" ) )
131-
{
132-
inRequest = true;
133-
}
134-
else if ( currentLine.equals( "#request end" ) )
135-
{
136-
if ( !inRequest )
137-
{
138-
throw new RuntimeException( "Request end not expected" );
139-
}
140-
try
141-
{
142-
processRequest( request.toString() );
143-
}
144-
catch ( Exception e )
145-
{
146-
if ( e instanceof Neo4jException )
147-
{
148-
// Error to track
149-
String id = testkitState.newId();
150-
testkitState.getErrors().put( id, (Neo4jException) e );
151-
writeResponse( driverError( id, (Neo4jException) e ) );
152-
System.out.println( "Neo4jException: " + e );
153-
}
154-
else if ( isConnectionPoolClosedException( e ) || e instanceof UntrustedServerException )
155-
{
156-
String id = testkitState.newId();
157-
DriverError driverError = DriverError.builder()
158-
.data(
159-
DriverError.DriverErrorBody.builder()
160-
.id( id )
161-
.errorType( e.getClass().getName() )
162-
.msg( e.getMessage() )
163-
.build()
164-
)
165-
.build();
166-
writeResponse( driverError );
167-
}
168-
else
169-
{
170-
// Unknown error, interpret this as a backend error.
171-
// Report to frontend and rethrow, note that if socket been
172-
// closed the writing will throw itself...
173-
writeResponse( BackendError.builder().data( BackendError.BackendErrorBody.builder().msg( e.toString() ).build() ).build() );
174-
// This won't print if there was an IO exception since line above will rethrow
175-
e.printStackTrace();
176-
throw e;
177-
}
178-
}
179-
return true;
180-
}
181-
else
182-
{
183-
if ( !inRequest )
184-
{
185-
throw new RuntimeException( "Command Received whilst not in request" );
186-
}
187-
request.append( currentLine );
188-
}
189-
}
190-
}
191-
192-
private DriverError driverError( String id, Neo4jException e )
193-
{
194-
return DriverError.builder().data(
195-
DriverError.DriverErrorBody.builder()
196-
.id( id )
197-
.errorType( e.getClass().getName() )
198-
.code( e.code() )
199-
.msg( e.getMessage() )
200-
.build() )
201-
.build();
202-
}
203-
204-
public void processRequest( String request )
205-
{
206-
System.out.println( "request = " + request + ", in = " + in + ", out = " + out );
207-
try
208-
{
209-
TestkitRequest testkitMessage = objectMapper.readValue( request, TestkitRequest.class );
210-
TestkitResponse response = testkitMessage.process( testkitState );
211-
if ( response != null )
212-
{
213-
writeResponse( response );
214-
}
215-
}
216-
catch ( IOException e )
217-
{
218-
throw new UncheckedIOException( e );
219-
}
220-
}
221-
222-
private void writeResponse( TestkitResponse response )
223-
{
224-
try
225-
{
226-
String responseStr = objectMapper.writeValueAsString( response );
227-
System.out.println("response = " + responseStr + ", in = " + in + ", out = " + out);
228-
write( "#response begin\n" );
229-
write( responseStr + "\n" );
230-
write( "#response end\n" );
231-
flush();
232-
}
233-
catch ( JsonProcessingException ex )
234-
{
235-
throw new RuntimeException( "Error writing response", ex );
236-
}
237-
}
52+
objectMapper.registerModule( testkitModule );
53+
objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
23854

239-
private boolean isConnectionPoolClosedException( Exception e )
240-
{
241-
return e instanceof IllegalStateException && e.getMessage() != null &&
242-
e.getMessage().equals( ConnectionPoolImpl.CONNECTION_POOL_CLOSED_ERROR_MESSAGE );
55+
InjectableValues injectableValues = new InjectableValues.Std( Collections.singletonMap( COMMAND_PROCESSOR_ID, processor ) );
56+
objectMapper.setInjectableValues( injectableValues );
57+
return objectMapper;
24358
}
24459
}

0 commit comments

Comments
 (0)