ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ptupit...@apache.org
Subject ignite git commit: IGNITE-3363 Forward Java output to the .NET console
Date Wed, 06 Jul 2016 14:50:57 GMT
Repository: ignite
Updated Branches:
  refs/heads/master b7d6ef7c1 -> 4e468a8cd


IGNITE-3363 Forward Java output to the .NET console

This closes #833


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4e468a8c
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4e468a8c
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4e468a8c

Branch: refs/heads/master
Commit: 4e468a8cda774b1712231a0c356431f5a0910e88
Parents: b7d6ef7
Author: Pavel Tupitsyn <ptupitsyn@apache.org>
Authored: Wed Jul 6 17:50:42 2016 +0300
Committer: Pavel Tupitsyn <ptupitsyn@apache.org>
Committed: Wed Jul 6 17:50:42 2016 +0300

----------------------------------------------------------------------
 .../platform/PlatformAbstractBootstrap.java     |   5 +
 .../processors/platform/PlatformBootstrap.java  |   6 +
 .../processors/platform/PlatformIgnition.java   |   7 +-
 .../callback/PlatformCallbackGateway.java       |  10 ++
 .../callback/PlatformCallbackUtils.java         |   8 +
 .../dotnet/PlatformDotNetBootstrap.java         |  12 ++
 .../dotnet/PlatformDotNetConsoleStream.java     |  54 ++++++
 .../cpp/jni/include/ignite/jni/exports.h        |   3 +
 .../platforms/cpp/jni/include/ignite/jni/java.h |   6 +
 modules/platforms/cpp/jni/project/vs/module.def |   2 +
 modules/platforms/cpp/jni/src/exports.cpp       |   8 +
 modules/platforms/cpp/jni/src/java.cpp          |  57 +++++-
 .../Apache.Ignite.Core.Tests.csproj             |   1 +
 .../ConsoleRedirectTest.cs                      | 177 +++++++++++++++++++
 .../Apache.Ignite.Core.Tests/TestRunner.cs      |  21 ++-
 .../Impl/Unmanaged/IgniteJniNativeMethods.cs    |   6 +
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        |  36 +++-
 .../Impl/Unmanaged/UnmanagedUtils.cs            |  16 ++
 18 files changed, 426 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
index 6fc46c4..a28677f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
@@ -49,6 +49,11 @@ public abstract class PlatformAbstractBootstrap implements PlatformBootstrap
{
         }
     }
 
+    /** {@inheritDoc} */
+    @Override public void init() {
+        // No-op.
+    }
+
     /**
      * Get configuration transformer closure.
      *

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
index 956a02d..07847a7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
@@ -36,4 +36,10 @@ public interface PlatformBootstrap {
      */
     public PlatformProcessor start(IgniteConfiguration cfg, @Nullable GridSpringResourceContext
springCtx,
         long envPtr, long dataPtr);
+
+    /**
+     * Init the bootstrap.
+     *
+     */
+    public void init();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
index 928aa66..d754b7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
@@ -61,6 +61,11 @@ public class PlatformIgnition {
         Thread.currentThread().setContextClassLoader(PlatformProcessor.class.getClassLoader());
 
         try {
+            PlatformBootstrap bootstrap = bootstrap(factoryId);
+
+            // This should be done before Spring XML initialization so that redirected stream
is picked up.
+            bootstrap.init();
+
             IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> cfg = configuration(springCfgPath);
 
             if (gridName != null)
@@ -68,8 +73,6 @@ public class PlatformIgnition {
             else
                 gridName = cfg.get1().getGridName();
 
-            PlatformBootstrap bootstrap = bootstrap(factoryId);
-
             PlatformProcessor proc = bootstrap.start(cfg.get1(), cfg.get2(), envPtr, dataPtr);
 
             PlatformProcessor old = instances.put(gridName, proc);

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackGateway.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackGateway.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackGateway.java
index 88532fc..41d3802 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackGateway.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackGateway.java
@@ -1040,6 +1040,16 @@ public class PlatformCallbackGateway {
     }
 
     /**
+     * Redirects the console output to platform.
+     *
+     * @param str String to write.
+     * @param isErr Whether this is stdErr or stdOut.
+     */
+    public static void consoleWrite(String str, boolean isErr) {
+        PlatformCallbackUtils.consoleWrite(str, isErr);
+    }
+
+    /**
      * Enter gateway.
      */
     protected void enter() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackUtils.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackUtils.java
index 7b36e5e..63c6682 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/callback/PlatformCallbackUtils.java
@@ -545,6 +545,14 @@ public class PlatformCallbackUtils {
     static native void affinityFunctionDestroy(long envPtr, long ptr);
 
     /**
+     * Redirects the console output.
+     *
+     * @param str String to write.
+     * @param isErr Whether this is stdErr or stdOut.
+     */
+    static native void consoleWrite(String str, boolean isErr);
+
+    /**
      * Private constructor.
      */
     private PlatformCallbackUtils() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
index 837ded9..9278246 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
@@ -20,11 +20,23 @@ package org.apache.ignite.internal.processors.platform.dotnet;
 import org.apache.ignite.internal.processors.platform.PlatformAbstractBootstrap;
 import org.apache.ignite.internal.processors.platform.PlatformAbstractConfigurationClosure;
 
+import java.io.PrintStream;
+
 /**
  * Interop .Net bootstrap.
  */
 public class PlatformDotNetBootstrap extends PlatformAbstractBootstrap {
     /** {@inheritDoc} */
+    @Override public void init() {
+        // Initialize console propagation.
+        // This call is idempotent, doing it on each node start is fine.
+        System.setOut(new PrintStream(new PlatformDotNetConsoleStream(false)));
+        System.setErr(new PrintStream(new PlatformDotNetConsoleStream(true)));
+
+        super.init();
+    }
+
+    /** {@inheritDoc} */
     @Override protected PlatformAbstractConfigurationClosure closure(long envPtr) {
         return new PlatformDotNetConfigurationClosure(envPtr);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConsoleStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConsoleStream.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConsoleStream.java
new file mode 100644
index 0000000..028c3ab
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConsoleStream.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.dotnet;
+
+import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Stream that writes to the .NET console.
+ */
+public class PlatformDotNetConsoleStream extends OutputStream {
+    /** Indicates whether this is an error stream. */
+    private final boolean isErr;
+
+    /**
+     * Ctor.
+     *
+     * @param err Error stream flag.
+     */
+    public PlatformDotNetConsoleStream(boolean err) {
+        isErr = err;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void write(byte[] b, int off, int len) throws IOException {
+        String s = new String(b, off, len);
+
+        PlatformCallbackGateway.consoleWrite(s, isErr);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void write(int b) throws IOException {
+        String s = String.valueOf((char) b);
+
+        PlatformCallbackGateway.consoleWrite(s, isErr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/cpp/jni/include/ignite/jni/exports.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/jni/include/ignite/jni/exports.h b/modules/platforms/cpp/jni/include/ignite/jni/exports.h
index bf278e2..3f400fb 100644
--- a/modules/platforms/cpp/jni/include/ignite/jni/exports.h
+++ b/modules/platforms/cpp/jni/include/ignite/jni/exports.h
@@ -181,6 +181,9 @@ extern "C" {
 
     bool IGNITE_CALL IgniteListenableCancel(gcj::JniContext* ctx, void* obj);
     bool IGNITE_CALL IgniteListenableIsCancelled(gcj::JniContext* ctx, void* obj);
+
+    void IGNITE_CALL IgniteSetConsoleHandler(gcj::ConsoleWriteHandler consoleHandler);
+    void IGNITE_CALL IgniteRemoveConsoleHandler(gcj::ConsoleWriteHandler consoleHandler);
 }
 
 #endif //_IGNITE_JNI_EXPORTS
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/cpp/jni/include/ignite/jni/java.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/jni/include/ignite/jni/java.h b/modules/platforms/cpp/jni/include/ignite/jni/java.h
index 13e6e8d..41d7caa 100644
--- a/modules/platforms/cpp/jni/include/ignite/jni/java.h
+++ b/modules/platforms/cpp/jni/include/ignite/jni/java.h
@@ -105,6 +105,8 @@ namespace ignite
             typedef void(JNICALL *AffinityFunctionAssignPartitionsHandler)(void* target,
long long ptr, long long inMemPtr, long long outMemPtr);
             typedef void(JNICALL *AffinityFunctionRemoveNodeHandler)(void* target, long long
ptr, long long memPtr);
             typedef void(JNICALL *AffinityFunctionDestroyHandler)(void* target, long long
ptr);
+            
+            typedef void(JNICALL *ConsoleWriteHandler)(const char* chars, int charsLen, unsigned
char isErr);
 
             /**
              * JNI handlers holder.
@@ -505,6 +507,8 @@ namespace ignite
                 static int Reallocate(long long memPtr, int cap);
                 static void Detach();
                 static void Release(jobject obj);
+                static void SetConsoleHandler(ConsoleWriteHandler consoleHandler);
+                static int RemoveConsoleHandler(ConsoleWriteHandler consoleHandler);
 
                 jobject IgnitionStart(char* cfgPath, char* name, int factoryId, long long
dataPtr);
                 jobject IgnitionStart(char* cfgPath, char* name, int factoryId, long long
dataPtr, JniErrorInfo* errInfo);
@@ -757,6 +761,8 @@ namespace ignite
             JNIEXPORT void JNICALL JniAffinityFunctionAssignPartitions(JNIEnv *env, jclass
cls, jlong envPtr, jlong ptr, jlong inMemPtr, jlong outMemPtr);
             JNIEXPORT void JNICALL JniAffinityFunctionRemoveNode(JNIEnv *env, jclass cls,
jlong envPtr, jlong ptr, jlong memPtr);
             JNIEXPORT void JNICALL JniAffinityFunctionDestroy(JNIEnv *env, jclass cls, jlong
envPtr, jlong ptr);
+            
+            JNIEXPORT void JNICALL JniConsoleWrite(JNIEnv *env, jclass cls, jstring str,
jboolean isErr);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/cpp/jni/project/vs/module.def
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/jni/project/vs/module.def b/modules/platforms/cpp/jni/project/vs/module.def
index cfe658c..ddddace 100644
--- a/modules/platforms/cpp/jni/project/vs/module.def
+++ b/modules/platforms/cpp/jni/project/vs/module.def
@@ -134,4 +134,6 @@ IgniteProcessorCreateNearCache @131
 IgniteProcessorGetOrCreateNearCache @132
 IgniteProcessorGetCacheNames @133
 IgniteProjectionForServers @134
+IgniteSetConsoleHandler @135
+IgniteRemoveConsoleHandler @136
  
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/cpp/jni/src/exports.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/jni/src/exports.cpp b/modules/platforms/cpp/jni/src/exports.cpp
index 6c4760c..2950d15 100644
--- a/modules/platforms/cpp/jni/src/exports.cpp
+++ b/modules/platforms/cpp/jni/src/exports.cpp
@@ -558,4 +558,12 @@ extern "C" {
     bool IGNITE_CALL IgniteListenableIsCancelled(gcj::JniContext* ctx, void* obj) {
         return ctx->ListenableIsCancelled(static_cast<jobject>(obj));
     }
+
+    void IGNITE_CALL IgniteSetConsoleHandler(gcj::ConsoleWriteHandler consoleHandler) {
+        gcj::JniContext::SetConsoleHandler(consoleHandler);
+    }
+
+    void IGNITE_CALL IgniteRemoveConsoleHandler(gcj::ConsoleWriteHandler consoleHandler)
{
+        gcj::JniContext::RemoveConsoleHandler(consoleHandler);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/cpp/jni/src/java.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/jni/src/java.cpp b/modules/platforms/cpp/jni/src/java.cpp
index 87f1e03..685afd3 100644
--- a/modules/platforms/cpp/jni/src/java.cpp
+++ b/modules/platforms/cpp/jni/src/java.cpp
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-#include <cstring>
 #include <string>
 #include <exception>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
 
 #include "ignite/jni/utils.h"
 #include "ignite/common/concurrent.h"
@@ -368,6 +370,8 @@ namespace ignite
             JniMethod M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_ASSIGN_PARTITIONS = JniMethod("affinityFunctionAssignPartitions",
"(JJJJ)V", true);
             JniMethod M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_REMOVE_NODE = JniMethod("affinityFunctionRemoveNode",
"(JJJ)V", true);
             JniMethod M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_DESTROY = JniMethod("affinityFunctionDestroy",
"(JJ)V", true);
+            
+            JniMethod M_PLATFORM_CALLBACK_UTILS_CONSOLE_WRITE = JniMethod("consoleWrite",
"(Ljava/lang/String;Z)V", true);
 
             const char* C_PLATFORM_UTILS = "org/apache/ignite/internal/processors/platform/utils/PlatformUtils";
             JniMethod M_PLATFORM_UTILS_REALLOC = JniMethod("reallocate", "(JI)V", true);
@@ -436,8 +440,10 @@ namespace ignite
 
             /* STATIC STATE. */
             gcc::CriticalSection JVM_LOCK;
+            gcc::CriticalSection CONSOLE_LOCK;
             JniJvm JVM;
             bool PRINT_EXCEPTION = false;
+            std::vector<ConsoleWriteHandler> consoleWriteHandlers;
 
             /* HELPER METHODS. */
 
@@ -828,7 +834,7 @@ namespace ignite
 
             void RegisterNatives(JNIEnv* env) {
                 {
-					JNINativeMethod methods[59];
+					JNINativeMethod methods[60];
 
                     int idx = 0;
 
@@ -910,6 +916,7 @@ namespace ignite
                     AddNativeMethod(methods + idx++, M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_ASSIGN_PARTITIONS,
reinterpret_cast<void*>(JniAffinityFunctionAssignPartitions));
                     AddNativeMethod(methods + idx++, M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_REMOVE_NODE,
reinterpret_cast<void*>(JniAffinityFunctionRemoveNode));
                     AddNativeMethod(methods + idx++, M_PLATFORM_CALLBACK_UTILS_AFFINITY_FUNCTION_DESTROY,
reinterpret_cast<void*>(JniAffinityFunctionDestroy));
+                    AddNativeMethod(methods + idx++, M_PLATFORM_CALLBACK_UTILS_CONSOLE_WRITE,
reinterpret_cast<void*>(JniConsoleWrite));
 
                     jint res = env->RegisterNatives(FindClass(env, C_PLATFORM_CALLBACK_UTILS),
methods, idx);
 
@@ -2501,6 +2508,35 @@ namespace ignite
                 }
             }
 
+            void JniContext::SetConsoleHandler(ConsoleWriteHandler consoleHandler) {
+                if (!consoleHandler)
+                    throw std::invalid_argument("consoleHandler can not be null");
+
+                CONSOLE_LOCK.Enter();
+                    
+                consoleWriteHandlers.push_back(consoleHandler);
+
+                CONSOLE_LOCK.Leave();
+            }
+
+            int JniContext::RemoveConsoleHandler(ConsoleWriteHandler consoleHandler) {
+                if (!consoleHandler)
+                    throw std::invalid_argument("consoleHandler can not be null");
+
+                CONSOLE_LOCK.Enter();
+
+                int oldSize = static_cast<int>(consoleWriteHandlers.size());
+                    
+                consoleWriteHandlers.erase(remove(consoleWriteHandlers.begin(), consoleWriteHandlers.end(),

+                    consoleHandler), consoleWriteHandlers.end());
+
+                int removedCnt = oldSize - static_cast<int>(consoleWriteHandlers.size());
+
+                CONSOLE_LOCK.Leave();
+
+                return removedCnt;
+            }
+
             void JniContext::ThrowToJava(char* msg) {
                 JNIEnv* env = Attach();
 
@@ -2867,6 +2903,23 @@ namespace ignite
             JNIEXPORT void JNICALL JniAffinityFunctionDestroy(JNIEnv *env, jclass cls, jlong
envPtr, jlong ptr) {
                 IGNITE_SAFE_PROC(env, envPtr, AffinityFunctionDestroyHandler, affinityFunctionDestroy,
ptr);
             }
+
+            JNIEXPORT void JNICALL JniConsoleWrite(JNIEnv *env, jclass cls, jstring str,
jboolean isErr) {
+                CONSOLE_LOCK.Enter();
+
+                if (consoleWriteHandlers.size() > 0) {
+                    ConsoleWriteHandler consoleWrite = consoleWriteHandlers.at(0);
+
+                    const char* strChars = env->GetStringUTFChars(str, nullptr);
+                    const int strCharsLen = env->GetStringUTFLength(str);
+
+                    consoleWrite(strChars, strCharsLen, isErr);
+
+                    env->ReleaseStringUTFChars(str, strChars);
+                }
+
+                CONSOLE_LOCK.Leave();
+            }
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index a601dbd..d0d1934 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -113,6 +113,7 @@
     <Compile Include="Compute\SerializableClosureTaskTest.cs" />
     <Compile Include="Compute\TaskAdapterTest.cs" />
     <Compile Include="Compute\TaskResultTest.cs" />
+    <Compile Include="ConsoleRedirectTest.cs" />
     <Compile Include="Dataload\DataStreamerTest.cs" />
     <Compile Include="Dataload\DataStreamerTestTopologyChange.cs" />
     <Compile Include="DataStructures\AtomicLongTest.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ConsoleRedirectTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ConsoleRedirectTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ConsoleRedirectTest.cs
new file mode 100644
index 0000000..bb44dcc
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ConsoleRedirectTest.cs
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests
+{
+    using System;
+    using System.IO;
+    using System.Text;
+    using System.Text.RegularExpressions;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Communication.Tcp;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests that Java console output is redirected to .NET console.
+    /// </summary>
+    public class ConsoleRedirectTest
+    {
+        /** */
+        private StringBuilder _outSb;
+
+        /** */
+        private StringBuilder _errSb;
+
+        /** */
+        private TextWriter _stdOut;
+
+        /** */
+        private TextWriter _stdErr;
+
+        /// <summary>
+        /// Sets up the test.
+        /// </summary>
+        [SetUp]
+        public void SetUp()
+        {
+            _stdOut = Console.Out;
+            _stdErr = Console.Error;
+
+            _outSb = new StringBuilder();
+            Console.SetOut(new StringWriter(_outSb));
+
+            _errSb = new StringBuilder();
+            Console.SetError(new StringWriter(_errSb));
+        }
+
+        /// <summary>
+        /// Tears down the test.
+        /// </summary>
+        [TearDown]
+        public void TearDown()
+        {
+            Console.SetOut(_stdOut);
+            Console.SetError(_stdErr);
+        }
+
+        /// <summary>
+        /// Tests the startup output.
+        /// </summary>
+        [Test]
+        public void TestStartupOutput()
+        {
+            using (Ignition.Start(TestUtils.GetTestConfiguration()))
+            {
+                Assert.IsTrue(_outSb.ToString().Contains("[ver=1, servers=1, clients=0,"));
+            }
+        }
+
+        /// <summary>
+        /// Tests startup error in Java.
+        /// </summary>
+        [Test]
+        public void TestStartupJavaError()
+        {
+            // Invalid config
+            Assert.Throws<IgniteException>(() =>
+                Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration())
+                {
+                    CommunicationSpi = new TcpCommunicationSpi
+                    {
+                        IdleConnectionTimeout = TimeSpan.MinValue
+                    }
+                }));
+
+            Assert.IsTrue(_errSb.ToString().Contains("SPI parameter failed condition check:
idleConnTimeout > 0"));
+        }
+
+        /// <summary>
+        /// Tests multiple appdomains and multiple console handlers.
+        /// </summary>
+        [Test]
+        public void TestMultipleDomains()
+        {
+            using (var ignite = Ignition.Start(TestUtils.GetTestConfiguration()))
+            {
+                Assert.IsTrue(_outSb.ToString().Contains("[ver=1, servers=1, clients=0,"));
+
+                // Run twice
+                RunInNewDomain();
+                RunInNewDomain();
+
+                Assert.AreEqual(5, ignite.GetCluster().TopologyVersion);
+
+                var outTxt = _outSb.ToString();
+
+                // Check output from another domain (2 started + 2 stopped = 4)
+                Assert.AreEqual(4, Regex.Matches(outTxt, ">>> Grid name: newDomainGrid").Count);
+
+                // Both domains produce the topology snapshot on node enter
+                Assert.AreEqual(2, Regex.Matches(outTxt, "ver=2, servers=2, clients=0,").Count);
+                Assert.AreEqual(1, Regex.Matches(outTxt, "ver=3, servers=1, clients=0,").Count);
+                Assert.AreEqual(2, Regex.Matches(outTxt, "ver=4, servers=2, clients=0,").Count);
+                Assert.AreEqual(1, Regex.Matches(outTxt, "ver=5, servers=1, clients=0,").Count);
+            }
+        }
+
+        /// <summary>
+        /// Runs the Ignite in a new domain.
+        /// </summary>
+        private static void RunInNewDomain()
+        {
+            AppDomain childDomain = null;
+
+            try
+            {
+                childDomain = AppDomain.CreateDomain("Child", null, new AppDomainSetup
+                {
+                    ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
+                    ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
+                    ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
+                    LoaderOptimization = LoaderOptimization.MultiDomainHost
+                });
+
+                var runner = (IIgniteRunner)childDomain.CreateInstanceAndUnwrap(
+                    typeof(IgniteRunner).Assembly.FullName, typeof(IgniteRunner).FullName);
+
+                runner.Run();
+            }
+            finally
+            {
+                if (childDomain != null)
+                    AppDomain.Unload(childDomain);
+            }
+        }
+
+        private interface IIgniteRunner
+        {
+            void Run();
+        }
+
+        private class IgniteRunner : MarshalByRefObject, IIgniteRunner
+        {
+            public void Run()
+            {
+                var ignite = Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration())
+                {
+                    GridName = "newDomainGrid"
+                });
+                Ignition.Stop(ignite.Name, true);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
index 149fa35..5403ebe 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
@@ -48,7 +48,7 @@ namespace Apache.Ignite.Core.Tests
                 return;
             }
 
-            TestOne(typeof(AffinityFunctionTest), "TestSimpleInheritance");
+            TestOne(typeof(ConsoleRedirectTest), "TestMultipleDomains");
 
             //TestAll(typeof (AffinityFunctionTest));
             //TestAllInAssembly();
@@ -56,7 +56,12 @@ namespace Apache.Ignite.Core.Tests
 
         private static int TestOne(Type testClass, string method)
         {
-            string[] args = { "/run:" + testClass.FullName + "." + method, Assembly.GetAssembly(testClass).Location
};
+            string[] args =
+            {
+                "/noshadow",
+                "/run:" + testClass.FullName + "." + method,
+                Assembly.GetAssembly(testClass).Location
+            };
 
             int returnCode = Runner.Main(args);
 
@@ -68,7 +73,11 @@ namespace Apache.Ignite.Core.Tests
 
         private static void TestAll(Type testClass)
         {
-            string[] args = { "/run:" + testClass.FullName, Assembly.GetAssembly(testClass).Location
};
+            string[] args =
+            {
+                "/noshadow",
+                "/run:" + testClass.FullName, Assembly.GetAssembly(testClass).Location
+            };
 
             int returnCode = Runner.Main(args);
 
@@ -78,7 +87,11 @@ namespace Apache.Ignite.Core.Tests
 
         private static void TestAllInAssembly()
         {
-            string[] args = { Assembly.GetAssembly(typeof(InteropMemoryTest)).Location };
+            string[] args =
+            {
+                "/noshadow",
+                Assembly.GetAssembly(typeof(InteropMemoryTest)).Location
+            };
 
             int returnCode = Runner.Main(args);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/IgniteJniNativeMethods.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/IgniteJniNativeMethods.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/IgniteJniNativeMethods.cs
index 4619080..2da4192 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/IgniteJniNativeMethods.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/IgniteJniNativeMethods.cs
@@ -429,5 +429,11 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         [DllImport(IgniteUtils.FileIgniteJniDll, EntryPoint = "IgniteListenableIsCancelled")]
         [return: MarshalAs(UnmanagedType.U1)]
         public static extern bool ListenableIsCancelled(void* ctx, void* target);
+
+        [DllImport(IgniteUtils.FileIgniteJniDll, EntryPoint = "IgniteSetConsoleHandler")]
+        public static extern void SetConsoleHandler(void* consoleHandler);
+
+        [DllImport(IgniteUtils.FileIgniteJniDll, EntryPoint = "IgniteRemoveConsoleHandler")]
+        public static extern int RemoveConsoleHandler(void* consoleHandler);
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
index ef901cb..4cbdf8f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
@@ -55,6 +55,13 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         Justification = "This class instance usually lives as long as the app runs.")]
     internal unsafe class UnmanagedCallbacks
     {
+        /** Console write delegate. */
+        private static readonly ConsoleWriteDelegate ConsoleWriteDel = ConsoleWrite;
+
+        /** Console write pointer. */
+        private static readonly void* ConsoleWritePtr = 
+            Marshal.GetFunctionPointerForDelegate(ConsoleWriteDel).ToPointer();
+
         /** Unmanaged context. */
         private volatile UnmanagedContext _ctx;
 
@@ -71,7 +78,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         /** Initialized flag. */
         private readonly ManualResetEventSlim _initEvent = new ManualResetEventSlim(false);
 
-        /** Actions to be called upon Ignite initialisation. */
+        /** Actions to be called upon Ignite initialization. */
         private readonly List<Action<Ignite>> _initActions = new List<Action<Ignite>>();
 
         /** GC handle to UnmanagedCallbacks instance to prevent it from being GCed. */
@@ -172,6 +179,8 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         private delegate void AffinityFunctionRemoveNodeDelegate(void* target, long ptr,
long memPtr);
         private delegate void AffinityFunctionDestroyDelegate(void* target, long ptr);
 
+        private delegate void ConsoleWriteDelegate(sbyte* chars, int charsLen, bool isErr);
+
         /// <summary>
         /// constructor.
         /// </summary>
@@ -1105,6 +1114,23 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
             });
         }
 
+        private static void ConsoleWrite(sbyte* chars, int charsLen, bool isErr)
+        {
+            try
+            {
+                var str = IgniteUtils.Utf8UnmanagedToString(chars, charsLen);
+
+                var target = isErr ? Console.Error : Console.Out;
+
+                target.Write(str);
+
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine("ConsoleWrite unmanaged callback failed: " + ex);
+            }
+        }
+
         #endregion
 
         #region AffinityFunction
@@ -1291,5 +1317,13 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
             
             _handleRegistry.Close();
         }
+
+        /// <summary>
+        /// Gets the console write handler.
+        /// </summary>
+        public static void* ConsoleWriteHandler
+        {
+            get { return ConsoleWritePtr; }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e468a8c/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs
index 924259c..0e9556d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.Core.Impl.Unmanaged
 {
     using System;
+    using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
     using System.Runtime.InteropServices;
     using Apache.Ignite.Core.Common;
@@ -48,6 +49,21 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
             if (ptr == IntPtr.Zero)
                 throw new IgniteException(string.Format("Failed to load {0}: {1}", 
                     IgniteUtils.FileIgniteJniDll, Marshal.GetLastWin32Error()));
+
+            AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload;
+
+            JNI.SetConsoleHandler(UnmanagedCallbacks.ConsoleWriteHandler);
+        }
+
+        /// <summary>
+        /// Handles the DomainUnload event of the current AppDomain.
+        /// </summary>
+        private static void CurrentDomain_DomainUnload(object sender, EventArgs e)
+        {
+            // Clean the handler to avoid JVM crash.
+            var removedCnt = JNI.RemoveConsoleHandler(UnmanagedCallbacks.ConsoleWriteHandler);
+
+            Debug.Assert(removedCnt == 1);
         }
 
         /// <summary>


Mime
View raw message