Skip to content

Commit 1886bfa

Browse files
authored
Merge pull request #370 from zhenlineo/1.3-byte-arrays
Byte arrays support
2 parents ebb653d + 4e33803 commit 1886bfa

21 files changed

+432
-63
lines changed

driver/src/main/java/org/neo4j/driver/internal/messaging/MessageFormat.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ interface Reader
4242

4343
}
4444

45-
Writer newWriter( WritableByteChannel ch );
45+
Writer newWriter( WritableByteChannel ch, boolean byteArraySupportEnabled );
4646

4747
Reader newReader( ReadableByteChannel ch );
4848

driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java

+19-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.neo4j.driver.internal.packstream.PackOutput;
3838
import org.neo4j.driver.internal.packstream.PackStream;
3939
import org.neo4j.driver.internal.packstream.PackType;
40+
import org.neo4j.driver.internal.packstream.ByteArrayIncompatiblePacker;
4041
import org.neo4j.driver.internal.util.Iterables;
4142
import org.neo4j.driver.internal.value.InternalValue;
4243
import org.neo4j.driver.internal.value.ListValue;
@@ -79,10 +80,10 @@ public class PackStreamMessageFormatV1 implements MessageFormat
7980
private static final Map<String,Value> EMPTY_STRING_VALUE_MAP = new HashMap<>( 0 );
8081

8182
@Override
82-
public MessageFormat.Writer newWriter( WritableByteChannel ch )
83+
public MessageFormat.Writer newWriter( WritableByteChannel ch, boolean byteArraySupportEnabled )
8384
{
8485
ChunkedOutput output = new ChunkedOutput( ch );
85-
return new Writer( output, output.messageBoundaryHook() );
86+
return new Writer( output, output.messageBoundaryHook(), byteArraySupportEnabled );
8687
}
8788

8889
@Override
@@ -106,11 +107,19 @@ public static class Writer implements MessageFormat.Writer, MessageHandler
106107
/**
107108
* @param output interface to write messages to
108109
* @param onMessageComplete invoked for each message, after it's done writing to the output
110+
* @param byteArraySupportEnabled specify if support to pack/write byte array to server
109111
*/
110-
public Writer( PackOutput output, Runnable onMessageComplete )
112+
public Writer( PackOutput output, Runnable onMessageComplete, boolean byteArraySupportEnabled )
111113
{
112114
this.onMessageComplete = onMessageComplete;
113-
packer = new PackStream.Packer( output );
115+
if( byteArraySupportEnabled )
116+
{
117+
packer = new PackStream.Packer( output );
118+
}
119+
else
120+
{
121+
packer = new ByteArrayIncompatiblePacker( output );
122+
}
114123
}
115124

116125
@Override
@@ -223,6 +232,10 @@ private void packValue( Value value ) throws IOException
223232
packer.packNull();
224233
break;
225234

235+
case BYTES_TyCon:
236+
packer.pack( value.asByteArray() );
237+
break;
238+
226239
case STRING_TyCon:
227240
packer.pack( value.asString() );
228241
break;
@@ -502,8 +515,6 @@ private Value unpackValue() throws IOException
502515
PackType type = unpacker.peekNextType();
503516
switch ( type )
504517
{
505-
case BYTES:
506-
break;
507518
case NULL:
508519
return value( unpacker.unpackNull() );
509520
case BOOLEAN:
@@ -512,6 +523,8 @@ private Value unpackValue() throws IOException
512523
return value( unpacker.unpackLong() );
513524
case FLOAT:
514525
return value( unpacker.unpackDouble() );
526+
case BYTES:
527+
return value( unpacker.unpackBytes() );
515528
case STRING:
516529
return value( unpacker.unpackString() );
517530
case MAP:

driver/src/main/java/org/neo4j/driver/internal/net/ChunkedOutput.java

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public class ChunkedOutput implements PackOutput
4040
/** Are currently in the middle of writing a chunk? */
4141
private boolean chunkOpen = false;
4242

43-
4443
public ChunkedOutput( WritableByteChannel ch )
4544
{
4645
this( 8192, ch );

driver/src/main/java/org/neo4j/driver/internal/net/SocketClient.java

+19-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
import static java.lang.String.format;
3636
import static java.nio.ByteOrder.BIG_ENDIAN;
37+
import static org.neo4j.driver.internal.util.ServerVersion.v3_2_0;
38+
import static org.neo4j.driver.internal.util.ServerVersion.version;
3739

3840
public class SocketClient
3941
{
@@ -123,9 +125,7 @@ public void start()
123125
{
124126
setChannel( ChannelFactory.create( address, securityPlan, timeoutMillis, logger ) );
125127
}
126-
protocol = negotiateProtocol();
127-
reader = protocol.reader();
128-
writer = protocol.writer();
128+
setProtocol( negotiateProtocol() );
129129
}
130130
catch ( ConnectException e )
131131
{
@@ -139,6 +139,21 @@ public void start()
139139
}
140140
}
141141

142+
public void updateProtocol( String serverVersion )
143+
{
144+
if( version( serverVersion ).lessThan( v3_2_0 ) )
145+
{
146+
setProtocol( SocketProtocolV1.createWithoutByteArraySupport( channel ) );
147+
}
148+
}
149+
150+
private void setProtocol( SocketProtocol protocol )
151+
{
152+
this.protocol = protocol;
153+
this.reader = protocol.reader();
154+
this.writer = protocol.writer();
155+
}
156+
142157
public void send( Queue<Message> messages ) throws IOException
143158
{
144159
int messageCount = 0;
@@ -255,7 +270,7 @@ private SocketProtocol negotiateProtocol() throws IOException
255270
{
256271
case VERSION1:
257272
logger.debug( "S: [HANDSHAKE] -> 1" );
258-
return new SocketProtocolV1( channel );
273+
return SocketProtocolV1.create( channel );
259274
case NO_VERSION:
260275
throw new ClientException( "The server does not support any of the protocol versions supported by " +
261276
"this driver. Ensure that you are using driver and server versions that " +

driver/src/main/java/org/neo4j/driver/internal/net/SocketConnection.java

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public void init( String clientName, Map<String,Value> authToken )
117117
queueMessage( new InitMessage( clientName, authToken ), initCollector );
118118
sync();
119119
this.serverInfo = new InternalServerInfo( socket.address(), initCollector.serverVersion() );
120+
socket.updateProtocol( serverInfo.version() );
120121
}
121122

122123
@Override
@@ -167,6 +168,7 @@ public synchronized void flush()
167168
}
168169
catch ( IOException e )
169170
{
171+
close();
170172
throw new ServiceUnavailableException( "Unable to send messages to server: " + e.getMessage(), e );
171173
}
172174
}

driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocolV1.java

+13-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919
package org.neo4j.driver.internal.net;
2020

21-
import java.io.IOException;
2221
import java.nio.channels.ByteChannel;
2322

2423
import org.neo4j.driver.internal.messaging.MessageFormat;
@@ -32,15 +31,22 @@ public class SocketProtocolV1 implements SocketProtocol
3231
private final Reader reader;
3332
private final Writer writer;
3433

35-
public SocketProtocolV1( ByteChannel channel ) throws IOException
34+
public static SocketProtocol create( ByteChannel channel )
3635
{
37-
messageFormat = new PackStreamMessageFormatV1();
36+
/*by default the byte array support is enabled*/
37+
return new SocketProtocolV1( channel, true );
38+
}
3839

39-
ChunkedOutput output = new ChunkedOutput( channel );
40-
BufferingChunkedInput input = new BufferingChunkedInput( channel );
40+
public static SocketProtocol createWithoutByteArraySupport( ByteChannel channel )
41+
{
42+
return new SocketProtocolV1( channel, false );
43+
}
4144

42-
this.writer = new PackStreamMessageFormatV1.Writer( output, output.messageBoundaryHook() );
43-
this.reader = new PackStreamMessageFormatV1.Reader( input, input.messageBoundaryHook() );
45+
private SocketProtocolV1( ByteChannel channel, boolean byteArraySupportEnabled )
46+
{
47+
messageFormat = new PackStreamMessageFormatV1();
48+
this.writer = messageFormat.newWriter( channel, byteArraySupportEnabled );
49+
this.reader = messageFormat.newReader( channel );
4450
}
4551

4652
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.packstream;
20+
21+
import java.io.IOException;
22+
23+
public class ByteArrayIncompatiblePacker extends PackStream.Packer
24+
{
25+
public ByteArrayIncompatiblePacker( PackOutput out )
26+
{
27+
super( out );
28+
}
29+
30+
public void packBytesHeader( int size ) throws IOException
31+
{
32+
throw new PackStream.UnPackable( "Packing bytes is not supported " +
33+
"as the current server this driver connected to does not support unpack bytes." );
34+
}
35+
}

driver/src/main/java/org/neo4j/driver/internal/packstream/PackStream.java

+27-29
Original file line numberDiff line numberDiff line change
@@ -501,39 +501,38 @@ public double unpackDouble() throws IOException
501501
throw new Unexpected( "Expected a double, but got: " + toHexString( markerByte ));
502502
}
503503

504-
public String unpackString() throws IOException
504+
public byte[] unpackBytes() throws IOException
505505
{
506506
final byte markerByte = in.readByte();
507-
if( markerByte == TINY_STRING ) // Note no mask, so we compare to 0x80.
507+
switch(markerByte)
508508
{
509-
return EMPTY_STRING;
509+
case BYTES_8: return unpackRawBytes( unpackUINT8() );
510+
case BYTES_16: return unpackRawBytes( unpackUINT16() );
511+
case BYTES_32:
512+
{
513+
long size = unpackUINT32();
514+
if ( size <= Integer.MAX_VALUE )
515+
{
516+
return unpackRawBytes( (int) size );
517+
}
518+
else
519+
{
520+
throw new Overflow( "BYTES_32 too long for Java" );
521+
}
522+
}
523+
default: throw new Unexpected( "Expected bytes, but got: 0x" + toHexString( markerByte & 0xFF ));
510524
}
511-
512-
return new String(unpackUtf8(markerByte), UTF_8);
513525
}
514526

515-
public byte[] unpackBytes() throws IOException
527+
public String unpackString() throws IOException
516528
{
517529
final byte markerByte = in.readByte();
518-
519-
switch(markerByte)
530+
if( markerByte == TINY_STRING ) // Note no mask, so we compare to 0x80.
520531
{
521-
case BYTES_8: return unpackBytes( unpackUINT8() );
522-
case BYTES_16: return unpackBytes( unpackUINT16() );
523-
case BYTES_32:
524-
{
525-
long size = unpackUINT32();
526-
if ( size <= Integer.MAX_VALUE )
527-
{
528-
return unpackBytes( (int) size );
529-
}
530-
else
531-
{
532-
throw new Overflow( "BYTES_32 too long for Java" );
533-
}
534-
}
535-
default: throw new Unexpected( "Expected binary data, but got: 0x" + toHexString( markerByte & 0xFF ));
532+
return EMPTY_STRING;
536533
}
534+
535+
return new String(unpackUtf8(markerByte), UTF_8);
537536
}
538537

539538
/**
@@ -558,17 +557,17 @@ private byte[] unpackUtf8(byte markerByte) throws IOException
558557
final byte markerHighNibble = (byte) (markerByte & 0xF0);
559558
final byte markerLowNibble = (byte) (markerByte & 0x0F);
560559

561-
if ( markerHighNibble == TINY_STRING ) { return unpackBytes( markerLowNibble ); }
560+
if ( markerHighNibble == TINY_STRING ) { return unpackRawBytes( markerLowNibble ); }
562561
switch(markerByte)
563562
{
564-
case STRING_8: return unpackBytes( unpackUINT8() );
565-
case STRING_16: return unpackBytes( unpackUINT16() );
563+
case STRING_8: return unpackRawBytes( unpackUINT8() );
564+
case STRING_16: return unpackRawBytes( unpackUINT16() );
566565
case STRING_32:
567566
{
568567
long size = unpackUINT32();
569568
if ( size <= Integer.MAX_VALUE )
570569
{
571-
return unpackBytes( (int) size );
570+
return unpackRawBytes( (int) size );
572571
}
573572
else
574573
{
@@ -608,7 +607,7 @@ private long unpackUINT32() throws IOException
608607
return in.readInt() & 0xFFFFFFFFL;
609608
}
610609

611-
private byte[] unpackBytes( int size ) throws IOException
610+
private byte[] unpackRawBytes(int size ) throws IOException
612611
{
613612
byte[] heapBuffer = new byte[size];
614613
in.readBytes( heapBuffer, 0, heapBuffer.length );
@@ -711,5 +710,4 @@ public UnPackable( String message )
711710
super( message );
712711
}
713712
}
714-
715713
}

driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19+
1920
package org.neo4j.driver.internal.summary;
2021

2122
import org.neo4j.driver.internal.net.BoltServerAddress;

driver/src/main/java/org/neo4j/driver/internal/types/InternalTypeSystem.java

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import static org.neo4j.driver.internal.types.TypeConstructor.NUMBER_TyCon;
3434
import static org.neo4j.driver.internal.types.TypeConstructor.PATH_TyCon;
3535
import static org.neo4j.driver.internal.types.TypeConstructor.RELATIONSHIP_TyCon;
36+
import static org.neo4j.driver.internal.types.TypeConstructor.BYTES_TyCon;
3637
import static org.neo4j.driver.internal.types.TypeConstructor.STRING_TyCon;
3738

3839
/**
@@ -47,6 +48,7 @@ public class InternalTypeSystem implements TypeSystem
4748

4849
private final TypeRepresentation anyType = constructType( ANY_TyCon );
4950
private final TypeRepresentation booleanType = constructType( BOOLEAN_TyCon );
51+
private final TypeRepresentation bytesType = constructType( BYTES_TyCon );
5052
private final TypeRepresentation stringType = constructType( STRING_TyCon );
5153
private final TypeRepresentation numberType = constructType( NUMBER_TyCon );
5254
private final TypeRepresentation integerType = constructType( INTEGER_TyCon );
@@ -76,6 +78,13 @@ public Type BOOLEAN()
7678
return booleanType;
7779
}
7880

81+
/** the Cypher type BYTES */
82+
@Override
83+
public Type BYTES()
84+
{
85+
return bytesType;
86+
}
87+
7988
/** the Cypher type STRING */
8089
@Override
8190
public Type STRING()

driver/src/main/java/org/neo4j/driver/internal/types/TypeConstructor.java

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ public String typeName()
4444
}
4545
},
4646

47+
BYTES_TyCon {
48+
@Override
49+
public String typeName()
50+
{
51+
return "BYTES";
52+
}
53+
},
54+
4755
STRING_TyCon {
4856
@Override
4957
public String typeName()

0 commit comments

Comments
 (0)