| package org.eclipse.basyx.vab.protocol.basyx.connector; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.SocketChannel; |
| |
| import org.eclipse.basyx.vab.exception.provider.ProviderException; |
| import org.eclipse.basyx.vab.protocol.api.IBaSyxConnector; |
| import org.eclipse.basyx.vab.protocol.basyx.CoderTools; |
| import org.eclipse.basyx.vab.protocol.basyx.server.VABBaSyxTCPInterface; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| |
| /** |
| * BaSyx connector class |
| * |
| * @author kuhn, pschorn, schnicke |
| * |
| */ |
| public class BaSyxConnector implements IBaSyxConnector { |
| |
| private Logger logger = LoggerFactory.getLogger(BaSyxConnector.class); |
| private InetSocketAddress serverSocketAddress; |
| private SocketChannel channelToProvider; |
| |
| |
| /** |
| * Constructor that creates a connection. |
| * |
| * This constructor connects to port at name. |
| */ |
| public BaSyxConnector(String hostName, int port) { |
| // Base constructor |
| super(); |
| |
| // Exception handling |
| try { |
| // Resolve address |
| InetAddress serverIPAddress = InetAddress.getByName(hostName); |
| serverSocketAddress = new InetSocketAddress(serverIPAddress, port); |
| } catch (IOException e) { |
| // Print stack trace |
| logger.error("Exception in BaSyxConnector", e); |
| } |
| } |
| |
| /** |
| * Close connection |
| */ |
| private void closeConnection() { |
| // Try to close connection |
| try { |
| channelToProvider.close(); |
| } catch (IOException e) { |
| // Print stack trace |
| logger.error("Exception in closeConnection", e); |
| } |
| } |
| |
| /** |
| * Invoke a BaSyx operation in a remote provider |
| */ |
| protected synchronized String invokeBaSyx(byte[] call) { |
| // Catch exceptions |
| try { |
| // Send byte array (BaSyx operation) via channel to provider |
| ByteBuffer txBuffer = ByteBuffer.wrap(call); |
| |
| // Channel to provider |
| channelToProvider = SocketChannel.open(); |
| // - Setup channel: set to blocking and connect to provider |
| channelToProvider.configureBlocking(true); |
| channelToProvider.connect(serverSocketAddress); |
| |
| channelToProvider.write(txBuffer); |
| |
| // Read response |
| // - Wait for leading 4 byte header that contains frame length |
| ByteBuffer rxBuffer1 = ByteBuffer.allocate(4); |
| // System.out.println("RX1"); |
| readBytes(rxBuffer1, 4); |
| // System.out.println("RX1-d"); |
| int frameSize = CoderTools.getInt32(rxBuffer1.array(), 0); |
| |
| // Wait for frame to arrive |
| ByteBuffer rxBuffer2 = ByteBuffer.allocate(frameSize); |
| // System.out.println("RX2"); |
| readBytes(rxBuffer2, frameSize); |
| // System.out.println("RX2-d"); |
| byte[] rxFrame = rxBuffer2.array(); |
| |
| // Return received data |
| |
| // Result check |
| if ((rxFrame == null) || (rxFrame.length < 2)) return null; |
| |
| // - FIXME: Check result on position 0 |
| |
| // Extract response |
| int jsonResultLen = CoderTools.getInt32(rxFrame, 1); |
| String jsonResult = new String(rxFrame, 1 + 4, jsonResultLen); |
| |
| // Close connection to prevent unused open channels |
| closeConnection(); |
| |
| // Return result |
| return jsonResult.toString(); |
| } catch (IOException e) { |
| // Print stack trace |
| logger.error("Exception in invokeBaSyx", e); |
| } |
| |
| // Indicate error |
| return null; |
| } |
| |
| |
| /** |
| * Read a number of bytes |
| */ |
| protected void readBytes(ByteBuffer bytes, int expectedBytes) { |
| // Exception handling |
| try { |
| // System.out.println("-Reading:"+expectedBytes); |
| // Read bytes until buffer is full |
| while (bytes.position() < expectedBytes) { |
| // System.out.println("-Pos:"+bytes.position()); |
| channelToProvider.read(bytes); |
| } |
| // System.out.println("-Read:"+expectedBytes); |
| } catch (IOException e) { |
| // Output exception |
| logger.error("Exception in readBytes", e); |
| } |
| } |
| |
| |
| /** |
| * Invoke a BaSys get operation via HTTP |
| */ |
| @Override |
| public String getModelPropertyValue(String servicePath) { |
| |
| byte[] call = createCall(servicePath, VABBaSyxTCPInterface.BASYX_GET); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| } |
| |
| |
| /** |
| * Invoke a Basys Set operation. Sets or overrides existing property, operation |
| * or event. |
| * |
| * @throws ProviderException |
| * that carries the Exceptions thrown on the server |
| */ |
| @Override |
| public String setModelPropertyValue(String servicePath, String newValue) { |
| |
| byte[] call = createCall(servicePath, newValue, VABBaSyxTCPInterface.BASYX_SET); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| } |
| |
| |
| /** |
| * Invoke a BaSys Create operation |
| */ |
| @Override |
| public String createValue(String servicePath, String newValue) throws ProviderException { |
| |
| byte[] call = createCall(servicePath, newValue, VABBaSyxTCPInterface.BASYX_CREATE); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| } |
| |
| |
| /** |
| * Invoke a Basys invoke operation. |
| */ |
| @Override |
| public String invokeOperation(String servicePath, String parameters) throws ProviderException { |
| |
| byte[] call = createCall(servicePath, parameters, VABBaSyxTCPInterface.BASYX_INVOKE); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| |
| } |
| |
| |
| /** |
| * Invoke a Basys delete operation. Deletes any resource under the given path |
| * |
| * @throws ProviderException |
| * that carries the Exceptions thrown on the server |
| */ |
| @Override |
| public String deleteValue(String servicePath) throws ProviderException { |
| |
| byte[] call = createCall(servicePath, VABBaSyxTCPInterface.BASYX_DELETE); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| } |
| |
| |
| /** |
| * Invoke a Basys delete operation. Deletes an entry from a map or collection by |
| * the given key |
| * |
| * @throws ProviderException |
| * that carries the Exceptions thrown on the server |
| */ |
| @Override |
| public String deleteValue(String servicePath, String jsonObject) throws ProviderException { |
| |
| byte[] call = createCall(servicePath, jsonObject, VABBaSyxTCPInterface.BASYX_DELETE); |
| |
| // Invoke BaSyx call and return result |
| return invokeBaSyx(call); |
| } |
| |
| |
| /** |
| * Create non-parameterized call that can be used as an argument to the |
| * invokeBaSyx function |
| * |
| * @param servicePath |
| * @param jsonObject |
| * @return |
| */ |
| private byte[] createCall(String servicePath, byte callType) { |
| // Create call |
| byte[] call = new byte[4 + 1 + 4 + servicePath.length()]; |
| // - Encode size does not include leading four bytes |
| CoderTools.setInt32(call, 0, call.length - 4); |
| // - Encode operation GET |
| CoderTools.setInt8(call, 4, callType); |
| // - Encode path length and path |
| CoderTools.setInt32(call, 5, servicePath.length()); |
| CoderTools.setString(call, 9, servicePath); |
| |
| return call; |
| } |
| |
| |
| /** |
| * Create parameterized byte call that can be used as an argument to the |
| * invokeBaSyx function |
| * |
| * @param servicePath |
| * @param newValue |
| * @param callType |
| * @return |
| */ |
| private byte[] createCall(String servicePath, String newValue, byte callType) { |
| |
| // Create call |
| byte[] call = new byte[4 + 1 + 4 + servicePath.length() + 4 + newValue.length()]; |
| // - Encode size does not include leading four bytes |
| CoderTools.setInt32(call, 0, call.length - 4); |
| // - Encode operation SET |
| CoderTools.setInt8(call, 4, callType); |
| // - Encode path |
| CoderTools.setInt32(call, 5, servicePath.length()); |
| CoderTools.setString(call, 9, servicePath); |
| // - Encode value |
| CoderTools.setInt32(call, 9 + servicePath.length(), newValue.length()); |
| CoderTools.setString(call, 9 + servicePath.length() + 4, newValue); |
| |
| return call; |
| } |
| |
| /** |
| * Get string representation of endpoint for given path for debugging. |
| * @param path Requested path |
| * @return String representing requested endpoint |
| */ |
| @Override |
| public String getEndpointRepresentation(String path) { |
| return "basyx://" + serverSocketAddress.getHostString() + ":" + serverSocketAddress.getPort() + "/" + path; |
| } |
| } |