예제 #1
0
        private object ReadCollection(Stream s)
        {
            object returnValue;
            char   listItemTypeAsChar = Convert.ToChar(s.ReadByte());
            int    numOfItemsInList   = SerDe.ReadInt32(s);

            switch (listItemTypeAsChar)
            {
            case 'c':
                var strArray = new string[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    strArray[itemIndex] = SerDe.ReadString(s);
                }
                returnValue = strArray;
                break;

            case 'i':
                var intArray = new int[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    intArray[itemIndex] = SerDe.ReadInt32(s);
                }
                returnValue = intArray;
                break;

            case 'g':
                var longArray = new long[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    longArray[itemIndex] = SerDe.ReadInt64(s);
                }
                returnValue = longArray;
                break;

            case 'd':
                var doubleArray = new double[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    doubleArray[itemIndex] = SerDe.ReadDouble(s);
                }
                returnValue = doubleArray;
                break;

            case 'A':
                var doubleArrayArray = new double[numOfItemsInList][];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    doubleArrayArray[itemIndex] = ReadCollection(s) as double[];
                }
                returnValue = doubleArrayArray;
                break;

            case 'b':
                var boolArray = new bool[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    boolArray[itemIndex] = Convert.ToBoolean(s.ReadByte());
                }
                returnValue = boolArray;
                break;

            case 'r':
                var byteArrayArray = new byte[numOfItemsInList][];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    int byteArrayLen = SerDe.ReadInt32(s);
                    byteArrayArray[itemIndex] = SerDe.ReadBytes(s, byteArrayLen);
                }
                returnValue = byteArrayArray;
                break;

            case 'j':
                var jvmObjectReferenceArray = new JvmObjectReference[numOfItemsInList];
                for (int itemIndex = 0; itemIndex < numOfItemsInList; ++itemIndex)
                {
                    string itemIdentifier = SerDe.ReadString(s);
                    jvmObjectReferenceArray[itemIndex] =
                        new JvmObjectReference(itemIdentifier, this);
                }
                returnValue = jvmObjectReferenceArray;
                break;

            default:
                // convert listItemTypeAsChar to UInt32 because the char may be non-printable
                throw new NotSupportedException(
                          string.Format("Identifier for list item type 0x{0:X} not supported",
                                        Convert.ToUInt32(listItemTypeAsChar)));
            }
            return(returnValue);
        }
예제 #2
0
        private object CallJavaMethod(
            bool isStatic,
            object classNameOrJvmObjectReference,
            string methodName,
            object[] args)
        {
            object         returnValue = null;
            ISocketWrapper socket      = null;

            try
            {
                MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream();
                payloadMemoryStream.Position = 0;
                PayloadHelper.BuildPayload(
                    payloadMemoryStream,
                    isStatic,
                    classNameOrJvmObjectReference,
                    methodName,
                    args);

                socket = GetConnection();

                Stream outputStream = socket.OutputStream;
                outputStream.Write(
                    payloadMemoryStream.GetBuffer(),
                    0,
                    (int)payloadMemoryStream.Position);
                outputStream.Flush();

                Stream inputStream        = socket.InputStream;
                int    isMethodCallFailed = SerDe.ReadInt32(inputStream);
                if (isMethodCallFailed != 0)
                {
                    string jvmFullStackTrace = SerDe.ReadString(inputStream);
                    string errorMessage      = BuildErrorMessage(
                        isStatic,
                        classNameOrJvmObjectReference,
                        methodName,
                        args);
                    _logger.LogError(errorMessage);
                    _logger.LogError(jvmFullStackTrace);
                    throw new Exception(errorMessage, new JvmException(jvmFullStackTrace));
                }

                char typeAsChar = Convert.ToChar(inputStream.ReadByte());
                switch (typeAsChar) // TODO: Add support for other types.
                {
                case 'n':
                    break;

                case 'j':
                    returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this);
                    break;

                case 'c':
                    returnValue = SerDe.ReadString(inputStream);
                    break;

                case 'i':
                    returnValue = SerDe.ReadInt32(inputStream);
                    break;

                case 'g':
                    returnValue = SerDe.ReadInt64(inputStream);
                    break;

                case 'd':
                    returnValue = SerDe.ReadDouble(inputStream);
                    break;

                case 'b':
                    returnValue = Convert.ToBoolean(inputStream.ReadByte());
                    break;

                case 'l':
                    returnValue = ReadCollection(inputStream);
                    break;

                default:
                    // Convert typeAsChar to UInt32 because the char may be non-printable.
                    throw new NotSupportedException(
                              string.Format(
                                  "Identifier for type 0x{0:X} not supported",
                                  Convert.ToUInt32(typeAsChar)));
                }
                _sockets.Enqueue(socket);
            }
            catch (Exception e)
            {
                _logger.LogException(e);
                socket?.Dispose();
                throw;
            }

            return(returnValue);
        }
예제 #3
0
        private object CallJavaMethod(
            bool isStatic,
            object classNameOrJvmObjectReference,
            string methodName,
            object[] args)
        {
            object         returnValue = null;
            ISocketWrapper socket      = null;

            try
            {
                Thread       thread = Thread.CurrentThread;
                MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream();
                payloadMemoryStream.Position = 0;
                PayloadHelper.BuildPayload(
                    payloadMemoryStream,
                    isStatic,
                    thread.ManagedThreadId,
                    classNameOrJvmObjectReference,
                    methodName,
                    args);

                socket = GetConnection();

                Stream outputStream = socket.OutputStream;
                outputStream.Write(
                    payloadMemoryStream.GetBuffer(),
                    0,
                    (int)payloadMemoryStream.Position);
                outputStream.Flush();

                _jvmThreadPoolGC.TryAddThread(thread);

                Stream inputStream        = socket.InputStream;
                int    isMethodCallFailed = SerDe.ReadInt32(inputStream);
                if (isMethodCallFailed != 0)
                {
                    string jvmFullStackTrace = SerDe.ReadString(inputStream);
                    string errorMessage      = BuildErrorMessage(
                        isStatic,
                        classNameOrJvmObjectReference,
                        methodName,
                        args);
                    _logger.LogError(errorMessage);
                    _logger.LogError(jvmFullStackTrace);
                    throw new Exception(errorMessage, new JvmException(jvmFullStackTrace));
                }

                char typeAsChar = Convert.ToChar(inputStream.ReadByte());
                switch (typeAsChar) // TODO: Add support for other types.
                {
                case 'n':
                    break;

                case 'j':
                    returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this);
                    break;

                case 'c':
                    returnValue = SerDe.ReadString(inputStream);
                    break;

                case 'i':
                    returnValue = SerDe.ReadInt32(inputStream);
                    break;

                case 'g':
                    returnValue = SerDe.ReadInt64(inputStream);
                    break;

                case 'd':
                    returnValue = SerDe.ReadDouble(inputStream);
                    break;

                case 'b':
                    returnValue = Convert.ToBoolean(inputStream.ReadByte());
                    break;

                case 'l':
                    returnValue = ReadCollection(inputStream);
                    break;

                default:
                    // Convert typeAsChar to UInt32 because the char may be non-printable.
                    throw new NotSupportedException(
                              string.Format(
                                  "Identifier for type 0x{0:X} not supported",
                                  Convert.ToUInt32(typeAsChar)));
                }
                _sockets.Enqueue(socket);
            }
            catch (Exception e)
            {
                _logger.LogException(e);

                if (e.InnerException is JvmException)
                {
                    // DotnetBackendHandler caught JVM exception and passed back to dotnet.
                    // We can reuse this connection.
                    _sockets.Enqueue(socket);
                }
                else
                {
                    // In rare cases we may hit the Netty connection thread deadlock.
                    // If max backend threads is 10 and we are currently using 10 active
                    // connections (0 in the _sockets queue). When we hit this exception,
                    // the socket?.Dispose() will not requeue this socket and we will release
                    // the semaphore. Then in the next thread (assuming the other 9 connections
                    // are still busy), a new connection will be made to the backend and this
                    // connection may be scheduled on the blocked Netty thread.
                    socket?.Dispose();
                }

                throw;
            }
            finally
            {
                _socketSemaphore.Release();
            }

            return(returnValue);
        }
예제 #4
0
        private object CallJavaMethod(
            bool isStatic,
            object classNameOrJvmObjectReference,
            string methodName,
            object[] args)
        {
            object         returnValue = null;
            ISocketWrapper socket      = null;

            try
            {
                // dotnet-interactive does not have a dedicated thread to process
                // code submissions and each code submission can be processed in different
                // threads. DotnetHandler uses the CLR thread id to ensure that the same
                // JVM thread is used to handle the request, which means that code submitted
                // through dotnet-interactive may be executed in different JVM threads. To
                // mitigate this, when running in the REPL, submit requests to the DotnetHandler
                // using the same thread id. This mitigation has some limitations in multithreaded
                // scenarios. If a JVM method is blocking and needs a JVM method call issued by a
                // separate thread to unblock it, then this scenario is not supported.
                //
                // ie, `StreamingQuery.AwaitTermination()` is a blocking call and requires
                // `StreamingQuery.Stop()` to be called to unblock it. However, the `Stop`
                // call will never run because DotnetHandler will assign the method call to
                // run on the same thread that `AwaitTermination` is running on.
                Thread       thread              = _isRunningRepl ? null : Thread.CurrentThread;
                int          threadId            = thread == null ? ThreadIdForRepl : thread.ManagedThreadId;
                MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream();
                payloadMemoryStream.Position = 0;

                PayloadHelper.BuildPayload(
                    payloadMemoryStream,
                    isStatic,
                    _processId,
                    threadId,
                    classNameOrJvmObjectReference,
                    methodName,
                    args);

                socket = GetConnection();

                Stream outputStream = socket.OutputStream;
                outputStream.Write(
                    payloadMemoryStream.GetBuffer(),
                    0,
                    (int)payloadMemoryStream.Position);
                outputStream.Flush();

                if (thread != null)
                {
                    _jvmThreadPoolGC.TryAddThread(thread);
                }

                Stream inputStream        = socket.InputStream;
                int    isMethodCallFailed = SerDe.ReadInt32(inputStream);
                if (isMethodCallFailed != 0)
                {
                    string jvmFullStackTrace = SerDe.ReadString(inputStream);
                    string errorMessage      = BuildErrorMessage(
                        isStatic,
                        classNameOrJvmObjectReference,
                        methodName,
                        args);
                    _logger.LogError(errorMessage);
                    _logger.LogError(jvmFullStackTrace);
                    throw new Exception(errorMessage, new JvmException(jvmFullStackTrace));
                }

                char typeAsChar = Convert.ToChar(inputStream.ReadByte());
                switch (typeAsChar) // TODO: Add support for other types.
                {
                case 'n':
                    break;

                case 'j':
                    returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this);
                    break;

                case 'c':
                    returnValue = SerDe.ReadString(inputStream);
                    break;

                case 'i':
                    returnValue = SerDe.ReadInt32(inputStream);
                    break;

                case 'g':
                    returnValue = SerDe.ReadInt64(inputStream);
                    break;

                case 'd':
                    returnValue = SerDe.ReadDouble(inputStream);
                    break;

                case 'b':
                    returnValue = Convert.ToBoolean(inputStream.ReadByte());
                    break;

                case 'l':
                    returnValue = ReadCollection(inputStream);
                    break;

                default:
                    // Convert typeAsChar to UInt32 because the char may be non-printable.
                    throw new NotSupportedException(
                              string.Format(
                                  "Identifier for type 0x{0:X} not supported",
                                  Convert.ToUInt32(typeAsChar)));
                }
                _sockets.Enqueue(socket);
            }
            catch (Exception e)
            {
                _logger.LogException(e);

                if (e.InnerException is JvmException)
                {
                    // DotnetBackendHandler caught JVM exception and passed back to dotnet.
                    // We can reuse this connection.
                    _sockets.Enqueue(socket);
                }
                else
                {
                    // In rare cases we may hit the Netty connection thread deadlock.
                    // If max backend threads is 10 and we are currently using 10 active
                    // connections (0 in the _sockets queue). When we hit this exception,
                    // the socket?.Dispose() will not requeue this socket and we will release
                    // the semaphore. Then in the next thread (assuming the other 9 connections
                    // are still busy), a new connection will be made to the backend and this
                    // connection may be scheduled on the blocked Netty thread.
                    socket?.Dispose();
                }

                throw;
            }
            finally
            {
                _socketSemaphore.Release();
            }

            return(returnValue);
        }
예제 #5
0
        /// <summary>
        /// Process the input and output streams.
        /// </summary>
        /// <param name="inputStream">The input stream.</param>
        /// <param name="outputStream">The output stream.</param>
        /// <param name="readComplete">True if stream is read completely, false otherwise.</param>
        /// <returns>The connection status.</returns>
        private ConnectionStatus ProcessStream(
            Stream inputStream,
            Stream outputStream,
            out bool readComplete)
        {
            readComplete = false;

            try
            {
                byte[] requestFlagBytes = SerDe.ReadBytes(inputStream, sizeof(int));
                // For socket stream, read on the stream returns 0, which
                // SerDe.ReadBytes() returns as null to denote the stream is closed.
                if (requestFlagBytes == null)
                {
                    return(ConnectionStatus.SOCKET_CLOSED);
                }

                // Check value of the initial request. Expected values are:
                // - CallbackFlags.CLOSE
                // - CallbackFlags.CALLBACK
                int requestFlag = BinaryPrimitives.ReadInt32BigEndian(requestFlagBytes);
                if (requestFlag == (int)CallbackFlags.CLOSE)
                {
                    return(ConnectionStatus.REQUEST_CLOSE);
                }
                else if (requestFlag != (int)CallbackFlags.CALLBACK)
                {
                    throw new Exception(
                              string.Format(
                                  "Unexpected callback flag received. Expected: {0}, Received: {1}.",
                                  CallbackFlags.CALLBACK,
                                  requestFlag));
                }

                // Use callback id to get the registered handler.
                int callbackId = SerDe.ReadInt32(inputStream);
                if (!_callbackHandlers.TryGetValue(
                        callbackId,
                        out ICallbackHandler callbackHandler))
                {
                    throw new Exception($"Unregistered callback id: {callbackId}");
                }

                s_logger.LogInfo(
                    string.Format(
                        "[{0}] Received request for callback id: {1}, callback handler: {2}",
                        ConnectionId,
                        callbackId,
                        callbackHandler));

                // Save contents of callback handler data to be used later.
                using var callbackDataStream =
                          new MemoryStream(SerDe.ReadBytes(inputStream, SerDe.ReadInt32(inputStream)));

                // Check the end of stream.
                int endOfStream = SerDe.ReadInt32(inputStream);
                if (endOfStream == (int)CallbackFlags.END_OF_STREAM)
                {
                    s_logger.LogDebug($"[{ConnectionId}] Received END_OF_STREAM signal.");

                    // Run callback handler.
                    callbackHandler.Run(callbackDataStream);

                    SerDe.Write(outputStream, (int)CallbackFlags.END_OF_STREAM);
                    readComplete = true;
                }
                else
                {
                    // This may happen when the input data is not read completely.
                    s_logger.LogWarn(
                        $"[{ConnectionId}] Unexpected end of stream: {endOfStream}.");

                    // Write flag to indicate the connection should be closed.
                    SerDe.Write(outputStream, (int)CallbackFlags.CLOSE);
                }

                return(ConnectionStatus.OK);
            }
            catch (Exception e)
            {
                s_logger.LogError($"[{ConnectionId}] ProcessStream() failed with exception: {e}");

                try
                {
                    SerDe.Write(outputStream, (int)CallbackFlags.DOTNET_EXCEPTION_THROWN);
                    SerDe.Write(outputStream, e.ToString());
                }
                catch (IOException)
                {
                    // JVM closed the socket.
                }
                catch (Exception ex)
                {
                    s_logger.LogError(
                        $"[{ConnectionId}] Writing exception to stream failed with exception: {ex}");
                }

                throw;
            }
        }