Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
91b16de
Add debug code
jonnyz32 Aug 7, 2025
953eed7
add support for all types
jonnyz32 Aug 7, 2025
8b6dc04
fix bug in getcolumntype
jonnyz32 Aug 7, 2025
b5de3bb
send data diretly as raw bytes
jonnyz32 Aug 7, 2025
7a3ffef
dont throw error if column types not exist
jonnyz32 Aug 7, 2025
31cc3f8
fix null ptr
jonnyz32 Aug 7, 2025
3aa7445
Increase max ws message size
jonnyz32 Aug 8, 2025
4da583e
Try to put blob in prepared stmt
jonnyz32 Aug 11, 2025
e98d9cc
add debug symbols
jonnyz32 Aug 11, 2025
287be45
Add logging in onwebsocketbinary
jonnyz32 Aug 11, 2025
eadeb0c
Upgrade jetty version
jonnyz32 Aug 11, 2025
5e2ce8b
change maven compiler target
jonnyz32 Aug 12, 2025
16e2f4c
change copy bytes
jonnyz32 Aug 12, 2025
a250d3f
fix some bugs
jonnyz32 Aug 12, 2025
d1cfb5e
fix some bugs
jonnyz32 Aug 12, 2025
24718ee
Increase max binary message size
jonnyz32 Aug 13, 2025
55f9e58
Use byte array input stream instead
jonnyz32 Aug 13, 2025
8990f2b
dont copy payload around
jonnyz32 Aug 13, 2025
509d982
fix bug
jonnyz32 Aug 13, 2025
78ebb85
use bloboffset
jonnyz32 Aug 13, 2025
4861c2e
use proper length
jonnyz32 Aug 13, 2025
e837264
dont convert blob to string
jonnyz32 Aug 13, 2025
36ffc12
Send response back as blob
jonnyz32 Aug 14, 2025
93a9b92
comment out some errors with binary sender
jonnyz32 Aug 14, 2025
6ee37d1
Check if instance of blob
jonnyz32 Aug 14, 2025
313ba36
get remote in method
jonnyz32 Aug 14, 2025
6e70c90
fix bug
jonnyz32 Aug 14, 2025
22cc52b
send data id back
jonnyz32 Aug 14, 2025
6fbaec6
id is actually a string
jonnyz32 Aug 14, 2025
062f05c
add id length as first byte
jonnyz32 Aug 14, 2025
e1dd33f
Got blob test kinda working
jonnyz32 Aug 17, 2025
4d3d0e7
Add test single blob
jonnyz32 Aug 18, 2025
f53069d
Add test multiple blobs in one row
jonnyz32 Aug 18, 2025
bcae6c2
Add more tests
jonnyz32 Aug 18, 2025
1bb4e5e
change blob length to 4 bytes
jonnyz32 Aug 18, 2025
f26ca51
Made row id 4 bytes
jonnyz32 Aug 19, 2025
a066ec0
test second buf of long blob
jonnyz32 Aug 19, 2025
8934795
dont use is.isavailable
jonnyz32 Aug 21, 2025
9ac009c
store blob locator
jonnyz32 Aug 21, 2025
7c9a5f4
Upgrade jt400
jonnyz32 Aug 21, 2025
6b22a0b
send blobs individually
jonnyz32 Aug 21, 2025
491d4ac
Bump buffer size to 4mb
jonnyz32 Aug 21, 2025
70e65e0
Move blobs needed out of rows
jonnyz32 Aug 21, 2025
6eaff73
change logic for inserting blobs
jonnyz32 Aug 22, 2025
57921ce
add other params as well
jonnyz32 Aug 22, 2025
5842b4c
Change preparesql logic
jonnyz32 Aug 22, 2025
22978b5
Move blobs needed count out of rows
jonnyz32 Aug 25, 2025
2f71e1e
send response data in seperate thread
jonnyz32 Aug 25, 2025
cf18f9a
put binary sender in seperate thread
jonnyz32 Aug 25, 2025
f70a0f9
use async sender
jonnyz32 Aug 25, 2025
3b9fb26
use async sender
jonnyz32 Aug 25, 2025
b3b60d5
wait until async send complete
jonnyz32 Aug 25, 2025
ba7882b
Make byte buffer larger
jonnyz32 Aug 25, 2025
f8dcda6
change back to 4mb
jonnyz32 Aug 25, 2025
27145e2
fix tests
jonnyz32 Aug 25, 2025
37f8543
cleanup
jonnyz32 Aug 26, 2025
69e75b1
cleanup
jonnyz32 Aug 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<jetty.version>9.4.54.v20240208</jetty.version>
<jetty.version>9.4.57.v20241219</jetty.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<defaultGoal>install</defaultGoal>
Expand Down Expand Up @@ -134,6 +136,14 @@
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>true</debug>
<debuglevel>lines,vars,source</debuglevel>
</configuration>
</plugin>
</plugins>
</pluginManagement>

Expand All @@ -149,6 +159,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand All @@ -169,7 +183,7 @@
<dependency>
<groupId>net.sf.jt400</groupId>
<artifactId>jt400</artifactId>
<version>11.2</version>
<version>21.0.5</version>
<!-- comment the <scope> tag for local development -->
<scope>provided</scope>
</dependency>
Expand Down Expand Up @@ -213,6 +227,25 @@
<artifactId>websocket-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version> <!-- use the latest -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version> <!-- use the latest -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
26 changes: 26 additions & 0 deletions src/main/java/com/github/ibm/mapepire/BlobRequestData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.github.ibm.mapepire;

public class BlobRequestData {

private int replacementIndex;
private int length;
private int offset;

public BlobRequestData(int replacementIndex, int length, int offset){
this.replacementIndex = replacementIndex;
this.length = length;
this.offset = offset;
}

public int getLength() {
return length;
}

public int getReplacementIndex() {
return replacementIndex;
}

public int getOffset() {
return offset;
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/github/ibm/mapepire/BlobResponseData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.github.ibm.mapepire;

import java.io.InputStream;
import java.sql.Blob;

public class BlobResponseData {
final Blob blob;
final String columnName;
final int rowId;
final int length;

public BlobResponseData(Blob is, String columnName, int rowId, int length){
this.blob = is;
this.columnName = columnName;
this.rowId = rowId;
this.length = length;
}

public Blob getBlob() {
return blob;
}

public String getColumnName(){
return columnName;
}

public int getRowId(){
return rowId;
}

public int getLength(){
return length;
}
}
8 changes: 2 additions & 6 deletions src/main/java/com/github/ibm/mapepire/ClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public abstract class ClientRequest implements Runnable {
private final SystemConnection m_conn;
private final String m_id;
private final DataStreamProcessor m_io;
protected final DataStreamProcessor m_io;
private final JsonObject m_reqObj;
private final Map<String, Object> replyData = new LinkedHashMap<String, Object>();

Expand All @@ -28,10 +28,6 @@ protected ClientRequest(final DataStreamProcessor _io, final SystemConnection _c
addReplyData("id", m_id);
}

public SystemConnection getConnection() {
return m_conn;
}

protected void addReplyData(final String _key, final Object _val) {
replyData.put(_key, _val);
}
Expand Down Expand Up @@ -126,7 +122,7 @@ private static String getErrorStringFromException(Throwable _e) {
return "Internal Error: " + _e.getClass().getSimpleName();
}

protected void sendreply() throws UnsupportedEncodingException, IOException {
protected void sendreply() throws IOException {
final Gson l = new GsonBuilder().serializeNulls().create();
final String json = l.toJson(replyData);
replyData.clear();
Expand Down
136 changes: 128 additions & 8 deletions src/main/java/com/github/ibm/mapepire/DataStreamProcessor.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
package com.github.ibm.mapepire;

import com.github.ibm.mapepire.requests.*;
import com.github.ibm.mapepire.ws.AsyncSender;
import com.github.theprez.jcmdutils.StringUtils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;

public class DataStreamProcessor implements Runnable {

private static final Object s_replyWriterLock = new String("Response Writer Lock");

private final SystemConnection m_conn;
private final BufferedReader m_in;
private final PrintStream m_out;
private final PrintStream m_out_text;

private final Map<String, RunSql> m_queriesMap = new HashMap<String, RunSql>();
private final Map<String, PrepareSql> m_prepStmtMap = new HashMap<String, PrepareSql>();
private final boolean m_isTestMode;
private final AsyncSender m_binarySender;

public DataStreamProcessor(final InputStream _in, final PrintStream _out, final SystemConnection _conn,
boolean _isTestMode)
public DataStreamProcessor(final InputStream _in, final PrintStream _outText, final AsyncSender binarySender, final SystemConnection _conn,
boolean _isTestMode)
throws UnsupportedEncodingException {
m_in = new BufferedReader(new InputStreamReader(_in, "UTF-8"));
m_out = _out;
m_out_text = _outText;
m_binarySender = binarySender;
m_conn = _conn;
m_isTestMode = _isTestMode;
}
Expand Down Expand Up @@ -66,6 +71,48 @@ public void run() {
}
}

private int bytesToInt(byte[] bytes, int offset, int length) {
int x = 0;
for (int i = offset; i < offset + length; i++) {
byte b = bytes[i];
x = x << 8 | b & 0xFF;
}
return x;
}


public void run(byte[] payload, int offset, int len) {
int cont_id_length = 2;
int cont_id = bytesToInt(payload, offset, cont_id_length);

List<BlobRequestData> blobRequestDataArray = new ArrayList<>();
int curOffset = offset + cont_id_length;
int replacementIndexLength = 1;
int sizeOfInt = 4;
while (curOffset < offset + len) {
int replacementIndex = bytesToInt(payload, curOffset, replacementIndexLength);
curOffset += replacementIndexLength;

int length = bytesToInt(payload, curOffset, sizeOfInt);
curOffset += sizeOfInt;

BlobRequestData blobRequestData = new BlobRequestData(replacementIndex, length, curOffset);
blobRequestDataArray.add(blobRequestData);
curOffset += length;
}

PrepareSql prev = m_prepStmtMap.get(String.valueOf(cont_id));
if (null == prev) {
dispatch(new BadReq(this, m_conn, null, "invalid correlation ID"));
return;
}
try {
RunBlob runBlob = new RunBlob(payload, blobRequestDataArray, prev);
} catch (Exception e) {
System.out.println("Caught exception " + e);
}
}

public void run(String requestString) {
final JsonElement reqElement;
final JsonObject reqObj;
Expand Down Expand Up @@ -179,12 +226,85 @@ public void run(String requestString) {

public void sendResponse(final String _response) throws UnsupportedEncodingException, IOException {
synchronized (s_replyWriterLock) {
m_out.write((_response + "\n").getBytes("UTF-8"));
m_out_text.write((_response + "\n").getBytes("UTF-8"));
Tracer.datastreamOut(_response);
m_out.flush();
m_out_text.flush();
}
}

public void sendResponse(final String id, final BlobResponseData blobResponseData) throws IOException, SQLException {
synchronized (s_replyWriterLock) {
int curOffset = 0;
byte[] buffer = new byte[4 * 1024 * 1024];
byte[] idBytes = id.getBytes(StandardCharsets.UTF_8);

curOffset = 0;
String columnName = blobResponseData.getColumnName();
InputStream is = blobResponseData.getBlob().getBinaryStream();
int rowId = blobResponseData.getRowId();

byte[] columnNameBytes = columnName.getBytes(StandardCharsets.UTF_8);

if (idBytes.length > 255) {
throw new IllegalArgumentException("ID too long to encode in one byte length");
}

// First byte is the length of the ID
buffer[curOffset] = (byte) idBytes.length;
curOffset += 1;

// Copy ID bytes after the length byte
System.arraycopy(idBytes, 0, buffer, curOffset, idBytes.length);
curOffset += idBytes.length;

// Copy rowId
ByteBuffer rowIdBuffer = ByteBuffer.allocate(4);
rowIdBuffer.putInt(rowId); // default is big-endian
byte[] rowIdBytes = rowIdBuffer.array();
for (int j = 0; j < 4; j++) {
buffer[curOffset] = rowIdBytes[j];
curOffset += 1;
}

// Copy column length
buffer[curOffset] = (byte) columnName.length();
curOffset += 1;

// copy column name
System.arraycopy(columnNameBytes, 0, buffer, curOffset, columnNameBytes.length);
curOffset += columnNameBytes.length;

// Copy blob length
ByteBuffer blobLength = ByteBuffer.allocate(4);
blobLength.putInt(blobResponseData.getLength()); // default is big-endian
byte[] bytes = blobLength.array();
for (int j = 0; j < 4; j++) {
buffer[curOffset] = bytes[j];
curOffset += 1;
}

int bytesRead = is.read(buffer, curOffset, buffer.length - curOffset);
curOffset += bytesRead;
int totalBytesRead = bytesRead;
if (bytesRead != -1) {
boolean isFinal = totalBytesRead == blobResponseData.getLength();
sendByteBuffer(buffer, curOffset, isFinal);
}

while ((bytesRead = is.read(buffer)) != -1) {
totalBytesRead += bytesRead;
boolean isFinal = totalBytesRead == blobResponseData.getLength();
sendByteBuffer(buffer, bytesRead, isFinal);
}
}
}

private void sendByteBuffer(byte[] buffer, int bytesRead, boolean isFinal) {
// Wrap only the bytes actually read
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead);
m_binarySender.send(byteBuffer, isFinal);
}

public void end() {
try {
m_conn.getJdbcConnection().close();
Expand Down
15 changes: 11 additions & 4 deletions src/main/java/com/github/ibm/mapepire/MapepireServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.github.ibm.mapepire.ws.DbWebsocketClient;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Server;
Expand Down Expand Up @@ -39,6 +40,10 @@ public static boolean isSingleMode() {
return s_isSingleMode;
}

private static int getNumBytesInMb(int num){
return num * 1024 * 1024;
}

public static void main(final String[] _args) {

final LinkedList<String> args = new LinkedList<String>();
Expand All @@ -63,9 +68,8 @@ public static void main(final String[] _args) {
if (testMode) {
System.setIn(new FileInputStream(testFile));
}
final DataStreamProcessor io = new DataStreamProcessor(System.in, System.out, conn, testMode);

io.run();
// final DataStreamProcessor io = new DataStreamProcessor(System.in, System.out, conn, testMode);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to figure out what to do here since the binarySender is relying on having a websocket.

// io.run();
} else {
s_isSingleMode = false;

Expand Down Expand Up @@ -170,12 +174,15 @@ public static void main(final String[] _args) {
(servletContext, nativeWebSocketConfiguration) -> {
nativeWebSocketConfiguration.getPolicy().setMaxTextMessageBufferSize(65535);
// Configure max message size
int maxWsMessageSize = 50 * 1024 * 1024; // 50MB
int maxWsMessageSize = getNumBytesInMb(200);
int maxBinaryMessageSize = getNumBytesInMb(200);
String maxWsMessageSizeStr = System.getenv("MAX_WS_MESSAGE_SIZE");
if (StringUtils.isNonEmpty(maxWsMessageSizeStr)) {
maxWsMessageSize = Integer.parseInt(maxWsMessageSizeStr);
}
nativeWebSocketConfiguration.getPolicy().setMaxTextMessageSize(maxWsMessageSize);
nativeWebSocketConfiguration.getPolicy().setMaxBinaryMessageSize(maxBinaryMessageSize);


// Add websockets
nativeWebSocketConfiguration.addMapping("/db/*", new DbSocketCreator());
Expand Down
Loading
Loading