IGNITE-6911 .NET: Optionally disable Java console redirect
authorPavel Tupitsyn <ptupitsyn@apache.org>
Thu, 16 Nov 2017 08:05:59 +0000 (11:05 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Thu, 16 Nov 2017 08:05:59 +0000 (11:05 +0300)
This closes #3039

14 files changed:
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformAbstractBootstrap.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformBootstrap.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformIgnition.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetBootstrap.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
modules/platforms/dotnet/Apache.Ignite.Core.Tests/ConsoleRedirectTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs
modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedUtils.cs

index 38cf36b..bf6caf2 100644 (file)
@@ -35,12 +35,7 @@ import org.jetbrains.annotations.Nullable;
 public abstract class PlatformAbstractBootstrap implements PlatformBootstrap {
     /** {@inheritDoc} */
     @Override public PlatformProcessor start(IgniteConfiguration cfg, @Nullable GridSpringResourceContext springCtx,
-        long envPtr, long dataPtr) {
-        final PlatformInputStream input = new PlatformExternalMemory(null, dataPtr).input();
-
-        Ignition.setClientMode(input.readBoolean());
-
-        processInput(input, cfg);
+        long envPtr) {
 
         IgniteConfiguration cfg0 = closure(envPtr).apply(cfg);
 
@@ -55,8 +50,12 @@ public abstract class PlatformAbstractBootstrap implements PlatformBootstrap {
     }
 
     /** {@inheritDoc} */
-    @Override public void init() {
-        // No-op.
+    @Override public void init(long dataPtr) {
+        final PlatformInputStream input = new PlatformExternalMemory(null, dataPtr).input();
+
+        Ignition.setClientMode(input.readBoolean());
+
+        processInput(input);
     }
 
     /**
@@ -71,9 +70,8 @@ public abstract class PlatformAbstractBootstrap implements PlatformBootstrap {
      * Processes any additional input data.
      *
      * @param input Input stream.
-     * @param cfg Config.
      */
-    protected void processInput(PlatformInputStream input, IgniteConfiguration cfg) {
+    protected void processInput(PlatformInputStream input) {
         // No-op.
     }
 }
index 07847a7..407e8c8 100644 (file)
@@ -31,15 +31,14 @@ public interface PlatformBootstrap {
      * @param cfg Configuration.
      * @param springCtx Optional Spring resource context.
      * @param envPtr Environment pointer.
-     * @param dataPtr Optional pointer to additional data required for startup.
      * @return Platform processor.
      */
-    public PlatformProcessor start(IgniteConfiguration cfg, @Nullable GridSpringResourceContext springCtx,
-        long envPtr, long dataPtr);
+    public PlatformProcessor start(IgniteConfiguration cfg, @Nullable GridSpringResourceContext springCtx, long envPtr);
 
     /**
      * Init the bootstrap.
      *
+     * @param dataPtr Optional pointer to additional data required for startup.
      */
-    public void init();
+    public void init(long dataPtr);
 }
\ No newline at end of file
index 754c69e..46c4a2d 100644 (file)
@@ -63,7 +63,7 @@ public class PlatformIgnition {
             PlatformBootstrap bootstrap = bootstrap(factoryId);
 
             // This should be done before Spring XML initialization so that redirected stream is picked up.
-            bootstrap.init();
+            bootstrap.init(dataPtr);
 
             IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> cfg = configuration(springCfgPath);
 
@@ -72,7 +72,7 @@ public class PlatformIgnition {
             else
                 igniteInstanceName = cfg.get1().getIgniteInstanceName();
 
-            PlatformProcessor proc = bootstrap.start(cfg.get1(), cfg.get2(), envPtr, dataPtr);
+            PlatformProcessor proc = bootstrap.start(cfg.get1(), cfg.get2(), envPtr);
 
             PlatformProcessor old = instances.put(igniteInstanceName, proc);
 
index 84a4577..b53a2b8 100644 (file)
@@ -17,8 +17,6 @@
 
 package org.apache.ignite.internal.processors.platform.dotnet;
 
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.logger.platform.PlatformLogger;
 import org.apache.ignite.internal.processors.platform.PlatformAbstractBootstrap;
 import org.apache.ignite.internal.processors.platform.PlatformAbstractConfigurationClosure;
 import org.apache.ignite.internal.processors.platform.memory.PlatformInputStream;
@@ -29,24 +27,22 @@ 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();
-    }
+    private boolean useLogger;
 
     /** {@inheritDoc} */
     @Override protected PlatformAbstractConfigurationClosure closure(long envPtr) {
-        return new PlatformDotNetConfigurationClosure(envPtr);
+        return new PlatformDotNetConfigurationClosure(envPtr, useLogger);
     }
 
     /** {@inheritDoc} */
-    @Override protected void processInput(PlatformInputStream input, IgniteConfiguration cfg) {
-        if (input.readBoolean())
-            cfg.setGridLogger(new PlatformLogger());
+    @Override protected void processInput(PlatformInputStream input) {
+        useLogger = input.readBoolean();
+
+        if (input.readBoolean()) {
+            // 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)));
+        }
     }
 }
\ No newline at end of file
index 5b4f1cc..9ee2f5e 100644 (file)
@@ -53,6 +53,9 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
     /** */
     private static final long serialVersionUID = 0L;
 
+    /** Whether to use platform logger (when custom logger is defined on .NET side). */
+    private final boolean useLogger;
+
     /** Configuration. */
     private IgniteConfiguration cfg;
 
@@ -64,14 +67,16 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
      *
      * @param envPtr Environment pointer.
      */
-    public PlatformDotNetConfigurationClosure(long envPtr) {
+    public PlatformDotNetConfigurationClosure(long envPtr, boolean useLogger) {
         super(envPtr);
+
+        this.useLogger = useLogger;
     }
 
     /** {@inheritDoc} */
     @SuppressWarnings("deprecation")
     @Override protected void apply0(IgniteConfiguration igniteCfg) {
-        // 3. Validate and copy Interop configuration setting environment pointer along the way.
+        // Validate and copy Interop configuration setting environment pointer along the way.
         PlatformConfiguration interopCfg = igniteCfg.getPlatformConfiguration();
 
         if (interopCfg != null && !(interopCfg instanceof PlatformDotNetConfiguration))
@@ -85,15 +90,16 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
 
         memMgr = new PlatformMemoryManagerImpl(gate, 1024);
 
-        PlatformLogger userLogger = null;
+        PlatformLogger logger = null;
 
-        if (igniteCfg.getGridLogger() instanceof PlatformLogger) {
-            userLogger = (PlatformLogger)igniteCfg.getGridLogger();
-            userLogger.setGateway(gate);
+        if (useLogger) {
+            logger = new PlatformLogger();
+            logger.setGateway(gate);
+            igniteCfg.setGridLogger(logger);
         }
 
         PlatformDotNetConfigurationEx dotNetCfg0 = new PlatformDotNetConfigurationEx(dotNetCfg, gate, memMgr,
-            userLogger);
+            logger);
 
         igniteCfg.setPlatformConfiguration(dotNetCfg0);
 
@@ -103,7 +109,7 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
         if (ggHome != null)
             U.setIgniteHome(ggHome);
 
-        // 4. Callback to .Net.
+        // Callback to .Net.
         prepare(igniteCfg, dotNetCfg0);
 
         // Make sure binary config is right.
index b808bbe..24f519e 100644 (file)
@@ -23,7 +23,7 @@
               workDirectory='c:' JvmMaxMemoryMb='1024' MetricsLogFrequency='0:0:10' isDaemon='true' 
               isLateAffinityAssignment='false' springConfigUrl='c:\myconfig.xml' autoGenerateIgniteInstanceName='true' 
               peerAssemblyLoadingMode='CurrentAppDomain' longQueryWarningTimeout='1:2:3' isActiveOnStart='false' 
-              consistentId='someId012'>
+              consistentId='someId012' redirectJavaConsoleOutput='false'>
     <localhost>127.1.1.1</localhost>
     <binaryConfiguration compactFooter='false' keepDeserialized='true'>
         <nameMapper type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+NameMapper' bar='testBar' />
index ed556b4..2e389c3 100644 (file)
@@ -116,6 +116,35 @@ namespace Apache.Ignite.Core.Tests
         }
 
         /// <summary>
+        /// Tests the disabled redirect.
+        /// </summary>
+        [Test]
+        public void TestDisabledRedirect()
+        {
+            // Run test in new process because JVM is initialized only once.
+            const string envVar = "ConsoleRedirectTest.TestDisabledRedirect";
+
+            if (Environment.GetEnvironmentVariable(envVar) == "true")
+            {
+                var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration(false));
+                Assert.IsTrue(cfg.RedirectJavaConsoleOutput);
+
+                cfg.RedirectJavaConsoleOutput = false;
+
+                using (Ignition.Start(cfg))
+                {
+                    Assert.AreEqual("", _errSb.ToString());
+                    Assert.AreEqual("", _outSb.ToString());
+                }
+            }
+            else
+            {
+                Environment.SetEnvironmentVariable(envVar, "true");
+                TestUtils.RunTestInNewProcess(GetType().FullName, "TestDisabledRedirect");
+            }
+        }
+
+        /// <summary>
         /// Tests multiple appdomains and multiple console handlers.
         /// </summary>
         [Test]
index 4cd3760..27e55f3 100644 (file)
@@ -98,6 +98,7 @@ namespace Apache.Ignite.Core.Tests
             Assert.AreEqual(new TimeSpan(1, 2, 3), cfg.LongQueryWarningTimeout);
             Assert.IsFalse(cfg.IsActiveOnStart);
             Assert.AreEqual("someId012", cfg.ConsistentId);
+            Assert.IsFalse(cfg.RedirectJavaConsoleOutput);
 
             Assert.AreEqual("secondCache", cfg.CacheConfiguration.Last().Name);
 
index 03e8bf7..26caf53 100644 (file)
@@ -380,10 +380,20 @@ namespace Apache.Ignite.Core.Tests
             var proc = System.Diagnostics.Process.Start(procStart);
             Assert.IsNotNull(proc);
 
-            IgniteProcess.AttachProcessConsoleReader(proc);
+            try
+            {
+                IgniteProcess.AttachProcessConsoleReader(proc);
 
-            Assert.IsTrue(proc.WaitForExit(19000));
-            Assert.AreEqual(0, proc.ExitCode);
+                Assert.IsTrue(proc.WaitForExit(19000));
+                Assert.AreEqual(0, proc.ExitCode);
+            }
+            finally
+            {
+                if (!proc.HasExited)
+                {
+                    proc.Kill();
+                }
+            }
         }
 
         /// <summary>
index e890577..f8c42e5 100644 (file)
@@ -215,6 +215,11 @@ namespace Apache.Ignite.Core
         public const bool DefaultIsActiveOnStart = true;
 
         /// <summary>
+        /// Default value for <see cref="RedirectJavaConsoleOutput"/> property.
+        /// </summary>
+        public const bool DefaultRedirectJavaConsoleOutput = true;
+
+        /// <summary>
         /// Initializes a new instance of the <see cref="IgniteConfiguration"/> class.
         /// </summary>
         public IgniteConfiguration()
@@ -222,6 +227,7 @@ namespace Apache.Ignite.Core
             JvmInitialMemoryMb = DefaultJvmInitMem;
             JvmMaxMemoryMb = DefaultJvmMaxMem;
             ClientConnectorConfigurationEnabled = DefaultClientConnectorConfigurationEnabled;
+            RedirectJavaConsoleOutput = DefaultRedirectJavaConsoleOutput;
         }
 
         /// <summary>
@@ -760,6 +766,7 @@ namespace Apache.Ignite.Core
             AutoGenerateIgniteInstanceName = cfg.AutoGenerateIgniteInstanceName;
             PeerAssemblyLoadingMode = cfg.PeerAssemblyLoadingMode;
             LocalEventListeners = cfg.LocalEventListeners;
+            RedirectJavaConsoleOutput = cfg.RedirectJavaConsoleOutput;
 
             if (CacheConfiguration != null && cfg.CacheConfiguration != null)
             {
@@ -1420,5 +1427,21 @@ namespace Apache.Ignite.Core
         /// Gets or sets consistent globally unique node identifier which survives node restarts.
         /// </summary>
         public object ConsistentId { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether Java console output should be redirected
+        /// to <see cref="Console.Out"/> and <see cref="Console.Error"/>, respectively.
+        /// <para />
+        /// Default is <see cref="DefaultRedirectJavaConsoleOutput"/>.
+        /// <para />
+        /// Java code outputs valuable information to console. However, since that is Java console,
+        /// it is not possible to capture that output with standard .NET APIs (<see cref="Console.SetOut"/>).
+        /// As a result, many tools (IDEs, test runners) are not able to display Ignite console output in UI.
+        /// <para />
+        /// This property is enabled by default and redirects Java console output to standard .NET console.
+        /// It is recommended to disable this in production.
+        /// </summary>
+        [DefaultValue(DefaultRedirectJavaConsoleOutput)]
+        public bool RedirectJavaConsoleOutput { get; set; }
     }
 }
index 5050806..98607b6 100644 (file)
                     <xs:documentation>Whether client connector should be enabled (allow thin clients, ODBC and JDBC drivers to work with Ignite).</xs:documentation>
                 </xs:annotation>
             </xs:attribute>
+            <xs:attribute name="redirectJavaConsoleOutput" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Whether Java console output should be redirected to Console.Out and Console.Error.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
         </xs:complexType>
     </xs:element>
 </xs:schema>
index bc14fdd..41419ac 100644 (file)
@@ -248,7 +248,8 @@ namespace Apache.Ignite.Core
                 try
                 {
                     // 4. Initiate Ignite start.
-                    UU.IgnitionStart(env, cfg.SpringConfigUrl, gridName, ClientMode, cfg.Logger != null, cbs.IgniteId);
+                    UU.IgnitionStart(env, cfg.SpringConfigUrl, gridName, ClientMode, cfg.Logger != null, cbs.IgniteId,
+                        cfg.RedirectJavaConsoleOutput);
 
                     // 5. At this point start routine is finished. We expect STARTUP object to have all necessary data.
                     var node = _startup.Ignite;
@@ -549,6 +550,7 @@ namespace Apache.Ignite.Core
                     {
                         Assembly assembly = Assembly.LoadFrom(s);
 
+                        // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                         if (assembly != null)
                             continue;
                     }
index fd067b9..fd9fb79 100644 (file)
@@ -75,7 +75,11 @@ namespace Apache.Ignite.Core.Impl
 
                 // 2. Create unmanaged pointer.
                 var jvm = CreateJvm(cfg);
-                jvm.EnableJavaConsoleWriter();
+
+                if (cfg.RedirectJavaConsoleOutput)
+                {
+                    jvm.EnableJavaConsoleWriter();
+                }
 
                 var cbs = new UnmanagedCallbacks(log, jvm);
                 jvm.RegisterCallbacks(cbs);
index 58586a9..e355793 100644 (file)
@@ -30,7 +30,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         #region NATIVE METHODS: PROCESSOR
 
         internal static void IgnitionStart(Env env, string cfgPath, string gridName,
-            bool clientMode, bool userLogger, long igniteId)
+            bool clientMode, bool userLogger, long igniteId, bool redirectConsole)
         {
             using (var mem = IgniteManager.Memory.Allocate().GetStream())
             using (var cfgPath0 = env.NewStringUtf(cfgPath))
@@ -38,10 +38,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
             {
                 mem.WriteBool(clientMode);
                 mem.WriteBool(userLogger);
-
-                // Additional data.
-                mem.WriteBool(false);
-                mem.WriteBool(false);
+                mem.WriteBool(redirectConsole);
 
                 long* args = stackalloc long[5];
                 args[0] = cfgPath == null ? 0 : cfgPath0.Target.ToInt64();