Files
UniversalMusicPlayer/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java
Markil3 1321549ef3 Moved most of the message handling to other classes.
I'm finding that this code is being used multiple times, so inheritance seems to be warranted.
2021-07-29 09:11:33 -07:00

249 lines
7.7 KiB
Java

/*
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
*/
package edu.regis.universeplayer.addon;
import com.google.gson.*;
import edu.regis.universeplayer.browserCommands.MessageRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
/**
* The browser link serves as a communication between this process and the
* browser. It automatically converts information as needed as it passes it to
* and from the browser process.
*
* @author William Hubbard
* @version 0.1
*/
public class BrowserLink extends MessageRunner
{
private static final Logger logger = LoggerFactory.getLogger(BrowserLink.class);
public static final Gson gson = new Gson();
/**
* Creates a message runner.
*/
public BrowserLink()
{
super(System.in, System.out);
}
@Override
public byte[] serializeObject(Object message)
{
String val = gson.toJson(message);
return val.getBytes(StandardCharsets.UTF_8);
}
@Override
public Object deserializeObject(byte[] message) throws IOException
{
JsonElement val = gson.fromJson(new String(message, StandardCharsets.UTF_8), JsonElement.class);
return getMessage(val);
}
@Override
public void writeMessage(OutputStream out, int messageNum, byte[] message) throws IOException
{
String messageString;
JsonObject sendOb = new JsonObject();
sendOb.add("messageNum", new JsonPrimitive(messageNum));
sendOb.add("message", gson.fromJson(new String(message, StandardCharsets.UTF_8), JsonElement.class));
messageString = gson.toJson(sendOb);
message = messageString.getBytes(StandardCharsets.UTF_8);
ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
lengthBuffer.order(ByteOrder.nativeOrder());
logger.debug("Writing message {} {}", messageNum, messageString);
/*
* Writes the message length
*/
lengthBuffer.clear();
lengthBuffer.putInt(message.length);
out.write(lengthBuffer.array());
/*
* Writes the message
*/
out.write(message);
out.flush();
}
@Override
public byte[][] readMessage(InputStream in) throws IOException
{
int messageLength;
int messageNum;
int readLength;
byte[] message;
JsonObject messageJson;
ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
lengthBuffer.order(ByteOrder.nativeOrder());
lengthBuffer.clear();
readLength = in.read(lengthBuffer.array());
if (readLength == 0)
{
logger.debug("Input stream closed, no more messages.");
return null;
}
messageLength = lengthBuffer.getInt();
message = new byte[messageLength];
readLength = in.read(message);
if (readLength < message.length)
{
logger.warn("Message shorter than reported (expected {} bytes, got {} bytes)", message.length, readLength);
}
messageJson = gson.fromJson(new String(message, StandardCharsets.UTF_8), JsonObject.class);
messageNum = messageJson.get("messageNum").getAsInt();
message = gson.toJson(messageJson.get("message")).getBytes(StandardCharsets.UTF_8);
lengthBuffer.order(ByteOrder.BIG_ENDIAN);
/*
* Wipe out the buffer
*/
lengthBuffer.clear();
lengthBuffer.put(new byte[4]);
lengthBuffer.clear();
lengthBuffer.putInt(messageNum);
logger.debug("Reading message {}", messageNum);
return new byte[][] {lengthBuffer.array(), message};
}
/**
* Translates a JSON element into a Java object
*
* @param message - The message to translate.
* @return A java object, whether it be a primitive wrapper or an actual
* object.
* @throws IOException - Thrown if we cannot determine what type of JSON
* element was provided.
*/
private Object getMessage(JsonElement message) throws IOException
{
if (message.isJsonPrimitive())
{
return getPrimitiveMessage((JsonPrimitive) message);
}
else if (message.isJsonObject())
{
return getObjectMessage((JsonObject) message);
}
else if (message.isJsonArray())
{
return getArrayMessage((JsonArray) message);
}
else if (message.isJsonNull())
{
return null;
}
else
{
throw new IOException("Unknown JSON element " + message.getClass());
}
}
/**
* Translates a JSON primitive element into a Java object.
*
* @param message - The message to translate.
* @return A java object, whether it be a {@link Number} object, a
* {@link Boolean} object, or a {@link String} object.
* @throws IOException - Thrown if we cannot determine what type of JSON
* element was provided.
*/
private Object getPrimitiveMessage(JsonPrimitive message) throws IOException
{
if (message.isBoolean())
{
return message.getAsBoolean();
}
else if (message.isNumber())
{
return message.getAsNumber();
}
else if (message.isString())
{
return message.getAsString();
}
else
{
throw new IOException("Unknown JSON element " + message.getClass());
}
}
/**
* Translates a JSON object element into a Java object.
*
* @param message - The message to translate.
* @return A java object, or a {@link HashMap} containing objects if we
* cannot parse it.
*/
private Object getObjectMessage(JsonObject message)
{
HashMap<String, Object> type;
Class<?> clazz = null;
if (message.has("type"))
{
try
{
clazz = Class.forName(message.getAsJsonPrimitive("type").getAsString());
}
catch (ClassNotFoundException | ClassCastException e)
{
logger.error("Illegal class type " + message.get("type"), e);
}
}
if (clazz != null)
{
return gson.fromJson(message, clazz);
}
else
{
/*
* If all else fails, just return a standard hashmap. That should work.
*/
type = new HashMap<>();
message.entrySet().forEach(entry -> {
try
{
type.put(entry.getKey(), getMessage(entry.getValue()));
}
catch (IOException e)
{
logger.error("Could not parse message entry " + entry.getKey() + " (" + entry.getValue() + ")", e);
}
});
return type;
}
}
/**
* Translates a JSON array element into a Java array.
*
* @param message - The message to translate.
* @return A java array. It may contain a variety of element types.
* @throws IOException - Thrown if we cannot determine what type of JSON
* element was provided.
*/
private Object getArrayMessage(JsonArray message) throws IOException
{
Object[] returnValue = new Object[message.size()];
for (int i = 0, l = returnValue.length; i < l; i++)
{
returnValue[i] = getMessage(message.get(i));
}
return returnValue;
}
}