com.fluidbpm.ws.client.v1.websocket.AGenericListMessageHandler Maven / Gradle / Ivy
package com.fluidbpm.ws.client.v1.websocket;
import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.compress.CompressedResponse;
import com.fluidbpm.program.api.vo.ws.Error;
import com.fluidbpm.ws.client.FluidClientException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Base list message handler.
*
* @author jasonbruwer on 2016/03/11.
* @since 1.1
*
* @param The response object generic.
*
* @see CompletableFuture
*/
public abstract class AGenericListMessageHandler implements IMessageResponseHandler {
private WebSocketClient webSocketClient;
private final List returnValue;
private List errors;
private IMessageReceivedCallback messageReceivedCallback;
private boolean isConnectionClosed;
private Set expectedEchoMessagesBeforeComplete;
private CompletableFuture> completableFuture;
private boolean compressedResponse;
public static Charset CHARSET = null;
/**
* Constructor for SQLResultSet callbacks.
*
* @param messageReceivedCallbackParam The callback events.
* @param webSocketClientParam The web-socket client.
* @param compressedResponseParam Compress the SQL Result in Base-64.
*/
public AGenericListMessageHandler(
IMessageReceivedCallback messageReceivedCallbackParam,
WebSocketClient webSocketClientParam,
boolean compressedResponseParam
) {
this(messageReceivedCallbackParam, webSocketClientParam);
this.compressedResponse = compressedResponseParam;
}
/**
* Message handler with callback.
*
* @param messageReceivedCallbackParam The message callback observer.
* @param webSocketClientParam The web-socket client.
*/
public AGenericListMessageHandler(
IMessageReceivedCallback messageReceivedCallbackParam,
WebSocketClient webSocketClientParam
) {
this.messageReceivedCallback = messageReceivedCallbackParam;
this.webSocketClient = webSocketClientParam;
this.returnValue = new CopyOnWriteArrayList();
this.errors = new CopyOnWriteArrayList();
this.expectedEchoMessagesBeforeComplete = new CopyOnWriteArraySet();
this.isConnectionClosed = false;
}
/**
* Checks whether {@code this} message handler can process
* the message {@code messageParam}
*
* @param message The message to check for qualification.
* @return The JSONObject.
*
* @see JSONObject
*/
public Object doesHandlerQualifyForProcessing(String message) {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(message);
} catch (JSONException jsonExcept) {
throw new FluidClientException(
"Unable to parse ["+message+"]. "+ jsonExcept.getMessage(),
jsonExcept, FluidClientException.ErrorCode.JSON_PARSING);
}
Error fluidError = new Error(jsonObject);
if (fluidError.getErrorCode() > 0) return fluidError;
String echo = fluidError.getEcho();
if (this.expectedEchoMessagesBeforeComplete.contains(echo)) return jsonObject;
return null;
}
/**
* 'Handles' the message.
* If there was an error, the object will be Error
* If there was no error, the object will be JSONObject
*
* @param objectToProcess The message to handle in JSON format.
*
* @see Error
* @see JSONObject
*/
@Override
public void handleMessage(Object objectToProcess) {
//There is an error...
if (objectToProcess instanceof Error) {
Error fluidError = ((Error)objectToProcess);
this.errors.add(fluidError);
//Do a message callback...
if (this.messageReceivedCallback != null) this.messageReceivedCallback.errorMessageReceived(fluidError);
//If complete future is provided...
if (this.completableFuture != null) {
this.completableFuture.completeExceptionally(
new FluidClientException(fluidError.getErrorMessage(), fluidError.getErrorCode()));
}
} else {
//No Error...
JSONObject jsonObject = (JSONObject)objectToProcess;
//Uncompress the compressed response...
if (this.compressedResponse) {
CompressedResponse compressedResponse = new CompressedResponse(jsonObject);
byte[] compressedJsonList =
UtilGlobal.decodeBase64(compressedResponse.getDataBase64());
byte[] uncompressedJson = null;
try {
uncompressedJson = this.uncompress(compressedJsonList);
} catch (IOException eParam) {
throw new FluidClientException(
"I/O issue with uncompress. "+eParam.getMessage(),
eParam,
FluidClientException.ErrorCode.IO_ERROR);
}
jsonObject = new JSONObject(new String(uncompressedJson));
}
T messageForm = this.getNewInstanceBy(jsonObject);
//Add to the list of return values...
this.returnValue.add(messageForm);
//Do a message callback...
if (this.messageReceivedCallback != null) this.messageReceivedCallback.messageReceived(messageForm);
//Completable future is set, and all response messages received...
if (this.completableFuture != null) {
String echo = messageForm.getEcho();
if (echo != null && !echo.trim().isEmpty()) this.expectedEchoMessagesBeforeComplete.remove(echo);
if (this.webSocketClient.getSentMessages() == this.webSocketClient.getReceivedMessages()) {
//Sent and received messages match...
this.completableFuture.complete(this.returnValue);
} else if (this.expectedEchoMessagesBeforeComplete.isEmpty()) {
//All expected messages received...
this.completableFuture.complete(this.returnValue);
}
}
}
}
/**
* Retrieve the stored instance of {@code CompletableFuture}.
* @return local instance of CompletableFuture.
*
* @see CompletableFuture
*/
public CompletableFuture> getCF() {
if (this.completableFuture == null) this.completableFuture = new CompletableFuture<>();
return this.completableFuture;
}
/**
* Event for when connection is closed.
*/
@Override
public void connectionClosed() {
this.isConnectionClosed = true;
if (this.completableFuture != null) {
//If there was no error...
if (this.getErrors().isEmpty()) {
this.completableFuture.complete(this.returnValue);
} else {
//there was an error...
Error firstFluidError = this.getErrors().get(0);
this.completableFuture.completeExceptionally(new FluidClientException(
firstFluidError.getErrorMessage(),
firstFluidError.getErrorCode()));
}
}
}
/**
* Checks whether the connection to the server is closed.
*
* @return Whether connection is closed.
*/
public boolean isConnectionClosed() {
return this.isConnectionClosed;
}
/**
* Checks to see whether there are error responses.
*
* @return Whether there is an error.
*/
public boolean hasErrorOccurred()
{
return !this.errors.isEmpty();
}
/**
* Adds the {@code expectedMessageEchoParam} echo to expect as a
* return value before the Web Socket operation may be regarded as complete.
*
* @param expectedMessageEchoParam The echo to expect.
*/
public void addExpectedMessage(String expectedMessageEchoParam) {
if (expectedMessageEchoParam == null || expectedMessageEchoParam.trim().isEmpty()) return;
this.expectedEchoMessagesBeforeComplete.add(expectedMessageEchoParam);
}
/**
* Get the list of remaining messages to receive.
*
* @return The message waiting for responses.
*/
public Set getExpectedEchoMessagesBeforeComplete() {
return this.expectedEchoMessagesBeforeComplete;
}
/**
* Error listing.
*
* @return The error that occured.
*/
public List getErrors() {
return this.errors;
}
/**
* Create a new instance of {@code T}.
*
* @param jsonObjectParam The {@code JSONObject} to create an object from.
* @return An instance of {@code ABaseFluidJSONObject}.
*
* @see ABaseFluidJSONObject
* @see JSONObject
*/
public abstract T getNewInstanceBy(JSONObject jsonObjectParam);
/**
* Gets the size of the return value.
*
* @return Added Count.
*/
public int getAddedCount() {
return this.returnValue.size();
}
/**
* Gets a list of echo messages of the current return values.
*
* @return The return value echo messages.
*/
private List getEchoMessagesFromReturnValue() {
List returnListing = new ArrayList();
if (this.returnValue == null) return returnListing;
Iterator iterForReturnVal =
this.returnValue.iterator();
//Only add where the ECHO message is set...
while (iterForReturnVal.hasNext()) {
T returnVal = iterForReturnVal.next();
if (returnVal.getEcho() == null) continue;
returnListing.add(returnVal.getEcho());
}
return returnListing;
}
/**
* Checks the local return value echo messages if all
* of them contain {@code echoMessageParam}.
*
* @param echoMessage The echo messages to check.
*
* @return Whether local return value echo messages contain {@code echoMessageParam}.
*/
public boolean doReturnValueEchoMessageContainAll(
List echoMessage
) {
if (echoMessage == null || echoMessage.isEmpty()) return false;
List allReturnValueEchoMessages =
this.getEchoMessagesFromReturnValue();
for (String toCheckFor: echoMessage) {
if (!allReturnValueEchoMessages.contains(toCheckFor)) return false;
}
return true;
}
/**
* Clears the return value.
*/
public void clear() {
this.returnValue.clear();
}
/**
* Gets the return value.
*
* @return The return value listing.
*/
public List getReturnValue() {
return this.returnValue;
}
/**
* Uncompress the raw {@code compressedBytesParam}.
*
* @param compressedBytesParam The compressed bytes to uncompress.
*
* @return Uncompressed bytes.
*
* @throws IOException - If there is an issue during the un-compression.
*/
protected byte[] uncompress(byte[] compressedBytesParam) throws IOException {
return UtilGlobal.uncompress(compressedBytesParam, CHARSET);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy