B4J Library jShell library

The jShell library allows you to start other programs. It is based on Apache Commons Exec project: http://commons.apache.org/proper/commons-exec/index.html

The programs are always started asynchronously. ProcessComplete event is raised when the process completes.

The following code will run the Java program we previously created:

B4X:
Sub AppStart (Args() As String)
   Dim shl As Shell
   shl.Initialize("shl", "java", _
     Array As String("-cp", "curl.jar", "b4j.example.main", "http://www.b4x.com"))
   shl.WorkingDirectory = "C:\Users\H\Documents\B4J\Curl\Objects"
   shl.Run(10000) 'set a timeout of 10 seconds
   StartMessageLoop 'need to call this as this is a console app.
End Sub


Sub shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
   If Success AND ExitCode = 0 Then
     Log("Success")
     Log(StdOut)
   Else
     Log("Error: " & StdErr)
   End If
   ExitApplication
End Sub
The Shell library can be used in a UI app in the same way.


V1.30 is released - Includes a method to start a process and wait for it to complete. You should normally not use this method in a UI application as it will block the UI thread.
 
Last edited:

Derek Johnson

Active Member
Licensed User
Longtime User
Tested, not working :)
I can´t get any command to work, not even:

B4X:
shl.Initialize("shl","ls", Null)
'or
shl.Initialize("shl","/bin/bash", Array as String("ls"))

I get not return in Process Completed or does the command work

Help, Please !! :(:(:(:(:(
I really need to issue "commands" to linux

Try using this: (this is in a UI based app using Tiny Core Linux)

B4X:
Sub Test
    Dim shl As Shell
    shl.Initialize("shl", "sudo",  Array As String("ls"))
    shl.WorkingDirectory = "/"
    shl.Run(10000) 'set a timeout of 10 seconds
End Sub

Sub shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    If Success And ExitCode = 0 Then
        Log("Success")
        Syslog(StdOut)
    Else
        Log("Error: " & StdErr)
    End If
End Sub

Just figured this out!
 

Pedro Caldeira

Active Member
Licensed User
Longtime User
doesn't work.
Get Exit code -1 in the result and does nothing
I really need to run some scripts called from B4J, so any help is welcome

B4X:
shl.initialize("shl","sudo", Array as String("./gtPro.sh")
shl.WorkinDirectory = file.DirApp
shl.run(10000)
 

OliverA

Expert
Licensed User
Longtime User
Get Exit code -1 in the result and does nothing
What is the output of StdErr and StdOut?
B4X:
Sub shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut AsString, StdErr AsString)

Edit: What is the result of Success?
 

rgarnett1955

Active Member
Licensed User
Longtime User
Hi

I know this is an old thread, but I thought I would post this code as someone might find it useful. The code is a class that can be used to start various processes. I wrote it to start jRDC servers from clients that require the service. My jRDC server has an argument that is the config file that contains the ip adress, port, sql etc.

The class

Porcess Control Class:
Sub Class_Globals
    Private fx As JFX
    Private shl As Shell
    Private caller As Object
    Private callerTag As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(callerArg As Object, callerTagArg As Int, eventName As String, _
                        procName As String, wrkingDir As String, args As List)
    caller = callerArg
    callerTag = callerTagArg
    shl.Initialize(eventName, procName, args)
    shl.WorkingDirectory = wrkingDir
End Sub


Private Sub shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    Dim errorStruct As procError_t
    errorStruct.Initialize
    
    errorStruct.CallerTag = callerTag
    errorStruct.Success   = Success
    errorStruct.ExitCode  = ExitCode
    
    If Success And ExitCode = 0 Then
        Log("Success")
        Log(StdOut)
    Else
        Log("Error: " & StdErr)
    End If
    CallSub2(caller, "processError", errorStruct)
End Sub


Public Sub runProcess(time As Long)
    shl.Run(time)
End Sub


Public Sub stopProcess
    shl.KillProcess
End Sub


Example calling

Example:
Process Globals

    Private jrdcServerProcess As sysProcess
    
Sub AppStart (Form1 As Form, Args() As String)
...
    Args = Array As String("-jar", "jRDC.jar", "Port_17200_ShorttClock.properties")
    jrdcServerProcess.Initialize(Me, 1, "jRDC_17200", "W:\MyProgramFiles_W\Java_13_0_02\bin\javaw", "G:\ShorttClockJ_RdcServer\jRDC\Objects", Args)
    
    
Private Sub selectTab

    jrdcServerProcess.runProcess(-1) 'Run forever
    
Private Sub deselectTab
    jrdcServerProcess.stopProcess

I usually start my servers using the task scheduler, but for single user apps it's handy to integrate the starting of the server into the client.

I prefer to use the JRDC servers instead of native jSQL because you get remote access from other devices.

The jRDC code to incorporate the config file arg. is shown below:

jRDC Code to provide the config filename as an arg:
'Non-UI application (console / server application)
#Region  Project Attributes
    #AdditionalJar: sqlite-jdbc-3.30.1
    #CommandLineArgs: "Port_17200_ShorttClock.properties"
    #MergeLibraries: True
#End Region

'change based on the jdbc jar file
'#AdditionalJar: mysql-connector-java-5.1.27-bin
'#AdditionalJar: postgresql-9.4.1207

Sub Process_Globals
    Public srvr As Server
    Public rdcConnector1 As RDCConnector
    Public const VERSION As Float = 2.23
    Type DBCommand (Name As String, Parameters() As Object)
    Type DBResult (Tag As Object, Columns As Map, Rows As List)
    
    Public IPAddress As String
    Public configMapFileArg As String
    Public dirString As String
End Sub

Sub AppStart (Args() As String)
    Dim configMap As Map
    
    If Args.Length <> 1 Then
        Return
    End If
    
    'Get the configMap filename arg
    Try
        configMapFileArg = Args(0)
    Catch
        Return
    End Try

    configMap = File.ReadMap(File.DirAssets, configMapFileArg)
    
    If configMap.Size = 0 Then
        Return
    End If

    srvr.Initialize("")
    rdcConnector1.Initialize(configMap)
    srvr.Port = rdcConnector1.serverPort
    srvr.AddHandler("/test", "TestHandler", False)
    srvr.AddHandler("/rdc", "RDCHandler", False)
    srvr.Start
    Log($"jRDC is running (version = $1.2{VERSION})"$)
    StartMessageLoop

Hope someone finds this useful
Rob
End Sub
 
Top