Skip to content

Commit

Permalink
#70: PowerShell main process send KeyStroke on child process, added J…
Browse files Browse the repository at this point in the history
…NetPSCmdlet to consolidate API override on not externalizable cmdlets (#72)
  • Loading branch information
masesdevelopers authored Nov 2, 2022
1 parent 50a7e02 commit 134a886
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 11 deletions.
71 changes: 71 additions & 0 deletions src/net/JNetPSCore/Cmdlet/JNetPSCmdlet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2022 MASES s.r.l.
*
* Licensed 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.
*
* Refer to LICENSE for more information.
*/

using MASES.JCOBridge.C2JBridge;
using MASES.JNet;
using System.Management.Automation;
using System.Reflection;

namespace MASES.JNetPSCore.Cmdlet
{
/// <summary>
/// Base class to be extended in derived projects for all PS cmdlet which needs the core active
/// </summary>
public abstract class JNetPSCmdlet<TCore> : PSCmdlet
where TCore : JNetCore<TCore>
{
// This method gets called once for each cmdlet in the pipeline when the pipeline starts executing
protected override void BeginProcessing()
{
WriteVerbose("Begin of JNetPSCmdlet");
WriteVerbose(MyInvocation.Line);
}

// This method will be called for each input received from the pipeline to this cmdlet; if no input is received, this method is not called
protected sealed override void ProcessRecord()
{
if (!JNetPSHelper<TCore>.InstanceCreated)
{
WriteWarning("Engine was not started, cannot execute the command");
return;
}

try
{
ProcessCommand();
}
catch (TargetInvocationException tie)
{
WriteError(new ErrorRecord(tie.InnerException, tie.InnerException.Source, ErrorCategory.InvalidOperation, tie.InnerException));
}
catch (JCOBridge.C2JBridge.JVMInterop.JavaException je)
{
var newEx = je.Convert();
WriteError(new ErrorRecord(newEx, newEx.Source, ErrorCategory.InvalidOperation, newEx));
}
}

protected abstract void ProcessCommand();

// This method will be called once at the end of pipeline execution; if no input is received, this method is not called
protected override void EndProcessing()
{
WriteVerbose("End of JNetPSCmdlet");
}
}
}
10 changes: 9 additions & 1 deletion src/net/JNetPSCore/Cmdlet/JNetPSExternalizableCmdlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Refer to LICENSE for more information.
*/

using MASES.JCOBridge.C2JBridge;
using System;
using System.Management.Automation;

Expand Down Expand Up @@ -66,7 +67,14 @@ protected sealed override void ProcessRecord()
{
if (!this.IsExternal()) { this.InvokeExternal(); return; }
}
ProcessCommand();
try
{
ProcessCommand();
}
catch (JCOBridge.C2JBridge.JVMInterop.JavaException je)
{
throw je.Convert();
}
}

protected abstract void ProcessCommand();
Expand Down
8 changes: 1 addition & 7 deletions src/net/JNetPSCore/Cmdlet/NewObjectCmdletCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

namespace MASES.JNetPSCore.Cmdlet
{
public abstract class NewObjectCmdletCommandBase<TCmdlet, TCore> : JNetPSExternalizableCmdlet<TCmdlet>
public abstract class NewObjectCmdletCommandBase<TCmdlet, TCore> : JNetPSCmdlet<TCore>
where TCmdlet : NewObjectCmdletCommandBase<TCmdlet, TCore>
where TCore : JNetCore<TCore>
{
Expand All @@ -41,12 +41,6 @@ public abstract class NewObjectCmdletCommandBase<TCmdlet, TCore> : JNetPSExterna
// This method will be called for each input received from the pipeline to this cmdlet; if no input is received, this method is not called
protected override void ProcessCommand()
{
if (!JNetPSHelper<TCore>.InstanceCreated)
{
WriteWarning("Engine was not started, cannot execute the command");
return;
}

object[] args = Arguments;
if (Arguments != null)
{
Expand Down
63 changes: 60 additions & 3 deletions src/net/JNetPSCore/JNetPSHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using System.Linq;
using System.Management.Automation;
using System.Reflection;
using System.Threading;

namespace MASES.JNetPSCore
{
Expand Down Expand Up @@ -230,28 +231,62 @@ internal static void InvokeExternal<T>(this T cmdlet) where T : PSCmdlet
}

cmdlet.WriteVerbose($"Running: {psInfo.FileName} {psInfo.Arguments}");
using (var proc = System.Diagnostics.Process.Start(psInfo))
using (var proc = new System.Diagnostics.Process())
{
try
{
processExited = cancelKeyRaised = false;
Console.CancelKeyPress += Console_CancelKeyPress;
proc.StartInfo = psInfo;
proc.ErrorDataReceived += Proc_ErrorDataReceived;
proc.BeginErrorReadLine();
proc.OutputDataReceived += Proc_OutputDataReceived;
proc.BeginOutputReadLine();
proc.Exited += Proc_Exited;
proc.Start();
while (!proc.HasExited)
{
proc.StandardInput.Write(System.Console.In.Read());
if (Console.KeyAvailable)
{
var key = Console.ReadKey();
if (!processExited)
{
proc.StandardInput.Write(key.KeyChar);
}
}
else Thread.Yield();

if (cancelKeyRaised)
{
proc.Kill();
}
}
}
finally
{
proc.ErrorDataReceived -= Proc_ErrorDataReceived;
proc.OutputDataReceived -= Proc_OutputDataReceived;
Console.CancelKeyPress -= Console_CancelKeyPress;
proc.Exited += Proc_Exited;
}
cmdlet.WriteVerbose($"Return code is: {proc.ExitCode}");
}
}

static bool processExited = false;

private static void Proc_Exited(object sender, EventArgs e)
{
processExited = true;
}

static bool cancelKeyRaised = false;

private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
cancelKeyRaised = true;
}

private static void Proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
System.Console.WriteLine(e.Data);
Expand Down Expand Up @@ -305,6 +340,28 @@ public static string VerbName<T>() where T : System.Management.Automation.Cmdlet
var attribute = t.GetCustomAttributes(typeof(CmdletAttribute), false).First() as CmdletAttribute;
return attribute.VerbName;
}

/// <summary>
/// Wites full exception in <paramref name="e"/> raised from <paramref name="cmdlet"/>
/// </summary>
/// <param name="cmdlet">The <see cref="System.Management.Automation.Cmdlet"/> executing</param>
/// <param name="e">The base <see cref="Exception"/> raised</param>
public static void WriteExtendedError(this System.Management.Automation.Cmdlet cmdlet, Exception e)
{
var exception = e;
while (exception != null)
{
cmdlet.WriteError(new ErrorRecord(exception, exception.Source, ErrorCategory.InvalidOperation, exception));
if (exception is JVMBridgeException jvmException)
{
exception = jvmException.InnerException;
}
else
{
exception = exception.InnerException;
}
}
}
}

/// <summary>
Expand Down Expand Up @@ -411,7 +468,7 @@ public static void CreateGlobalInstance(System.Management.Automation.Cmdlet cmdl
{
lock (_instanceCreatedLock)
{
if (_instanceCreated) { cmdlet.WriteWarning("CreateGlobalInstance requested but it was previously requested."); return; }
if (_instanceCreated) { cmdlet.WriteWarning("A new CreateGlobalInstance requested, but it was previously requested."); return; }
cmdlet.WriteDebug("Invoking CreateGlobalInstance");
_ = typeof(TClass).RunStaticMethodOn(typeof(SetupJVMWrapper<>), nameof(JNetCore.CreateGlobalInstance));
cmdlet.WriteDebug("Invoked CreateGlobalInstance");
Expand Down

0 comments on commit 134a886

Please sign in to comment.