Bug #11271
server-side streams and files with frames/redirected terminal
100%
Related issues
History
#1 Updated by Constantin Asofiei 5 months ago
Currently, enabling server-side streams does not allow work with FRAMEs (i.e. redirected terminal). To make it work, this requires for FWD client to 'call back' to the FWD server to access the stream. I have some changes which I will commit to 11271a once I have them stable; the downside for this is that the FWD client has granular stream API calls, which in some cases may increase the network trips.
This also affects process streams, which will be on the server.
Alexandru: do you have some standalone tests for the behavior in #11184?
#2 Updated by Alexandru Lungu 5 months ago
- File start.p
added
Alexandru: do you have some standalone tests for the behavior in #11184?
Attached example run in Hotel.
- Project needs directory.xml changes.
- FWD requires some changes from #11184-6 (first patch), the second patch is not final - is what I thought is the closest to something working, but it is not :)
#3 Updated by Alexandru Lungu 5 months ago
- File gen_bmp.py
added
AH, it also requires a folder images/ with 100 generated bmps. Attached a script to generate them.
#6 Updated by Constantin Asofiei 4 months ago
For streams to work server-side, we need to also bring the file-system to the server-side. But OS-GETENV converts in FWD as FileSystemOps.getProperty() and executes via FileSystemDaemon.getProperty - and, if file-system is server-side, then the FWD client's env is not available.
So I need to move OS-GETENV to emit via EnvironmentOps and execute via EnvironmentDaemon.
#7 Updated by Constantin Asofiei 4 months ago
- Assignee set to Constantin Asofiei
- Status changed from New to Review
- reviewer Greg Shah added
Greg, please review 11271b rev 16483. I'm still going through testing. Thing to note is launcher can't be used on server-side easily (it requires terminal), but I need to finish testing full PASOE support.
Some notes on the implementation: both a network and a local instance of StreamDaemon is created, to allow bi-directional communication. These instances are wrapped inside a proxy (with the RemoteStreamHandler handler) which decides when and which streams to be moved from the remote side to the local side. So, if a stream uses UI and now is on the server, the client will first 'check it out' from the server (which flushes and closes it) and 'checks it on' on the client (where it gets opened and positioned correctly). The same approach is on the server-side - if the stream is on the client, access is moved to the server by close/open.
The implementation requires both streams and file-system to be on server-side - on a second thought, I think we need to force streams are on server-side if file-system is on server-side (and vice-versa).
Some metrics for this simple test with 100k iterations:
def var ch as char. def var i as int. def var n1 as int64. def var n2 as int64. n1 = mtime. form ch with frame f1 side-labels. def stream rpt. output stream rpt to a.txt. do i = 1 to 100000: ch = string(i). display stream rpt ch with frame f1. down stream rpt with frame f1. put stream rpt unformatted ch skip. end. output stream rpt close. n2 = mtime.With server-side streams:
- display + put: 24390ms
- only put: 76ms
- only display: 7662ms
- display + put: 11784ms
- only put: 4298ms
- only display: 7503ms
The complex DISPLAY+PUT takes ~2 times more unfortunately, when executed with server-side streams. This is because each iteration requires the stream to be moved from one side to the other a couple of times. I'll look to find if I can improve this.
#8 Updated by Constantin Asofiei 4 months ago
Alex, I've ran your image load test and I don't see any difference with server-side file-system - it takes ~0.5s in both cases. The latest changes are in 11271b rev 16485 (had to fix some other stuff related to server-side file-system, for APIs which are still called from client).
#9 Updated by Constantin Asofiei 4 months ago
Constantin Asofiei wrote:
Alex, I've ran your image load test and I don't see any difference with server-side file-system - it takes ~0.5s in both cases. The latest changes are in 11271b rev 16485 (had to fix some other stuff related to server-side file-system, for APIs which are still called from client).
I've done some debugging now and there is something lets say 'out of order': ButtonGuiImpl.loadImage creates a java FileInputStream - so this does not use 'server-side' file-system or streams, even if enabled. I've tested in Swing for now, I'll check also in Web GUI.
#10 Updated by Constantin Asofiei 4 months ago
Tested also in Web GUI, times are similar to Swing, in both cases.
#11 Updated by Constantin Asofiei 4 months ago
- % Done changed from 0 to 100
#13 Updated by Greg Shah 4 months ago
Code Review Task Branch 11271a Revision 16469
1. Let's find a different name than IStream for the interface. I like the changes to abstract that, but the name is too dotnet. Perhaps BaseStream or something like that could be better?
2. The StreamDaemon on the server side is a singleton, which means we must synchronize all of the state access and modification. Please make the class fully thread-safe.
#15 Updated by Constantin Asofiei 4 months ago
Greg Shah wrote:
Code Review Task Branch 11271a Revision 16469
Sorry, the branch to review is 11271b rev 16492 as of now.
1. Let's find a different name than
IStreamfor the interface. I like the changes to abstract that, but the name is too dotnet. PerhapsBaseStreamor something like that could be better?
In 11271a, yes, I tried to extract APIs which need to be executed by the client-side. But this is not used in 11271b. This needs to be further refactored at some point, to know exactly which APIs are executed by the client and server side.
2. The
StreamDaemonon the server side is a singleton, which means we must synchronize all of the state access and modification. Please make the class fully thread-safe.
In 11271b there is no state in StreamDaemon when on server-side: resolveWorkArea is overridden in ServerStreamDaemon to be context-local. ServerSideTerminal is also context-local.
#16 Updated by Hynek Cihlar 4 months ago
Code review 11271b.
FileStream.restoreStateAndOpen always reopens with append=true. What if the original stream is opened with append=false?
resolveId will NPE on unknown streams — StreamDaemon.java
The new resolveId method returns primitive int, but wa.ids.get(stream) returns Integer which can be null. The original code in reverseLookup safely handled null via Integer id = ids.get(s).
Swapped Javadoc on serialization methods — Stream.java and FileStream.java
In both classes, the Javadoc for readExternal (diff lines 1017, 617) says "Serialize this stream's state" when it actually deserializes/restores. Conversely, writeExternal (diff lines 1079, 655) says "Restore this stream's state" when it actually serializes. The comments are transposed.
Should access to RemoteStreamHandler.remoteStreams be synchronized?
Double close in RemoteStreamHandler.invoke for file resource streams
streamClose is true and the stream is a local file resource, the code:
- Calls
local.checkOutStream(id)(closes local file handle) - Calls
remote.checkInStream(id, state)(opens on remote) - Calls
method.invoke(remote, args)(close on remote) - Then falls through to
method.invoke(handler, args)wherehandler local— executing close logic again on the already-checked-out local stream
FileSystemDaemon.singleton used without null guard in static methods.
RuntimeException wrapping in restoreStateAndOpen/saveStateAndClose bypasses 4GL error condition handling. Both methods catch IOException/ClassNotFoundException and wrap them in raw RuntimeException (diff lines 377–385, 417–419). If a file cannot be re-opened during stream migration (e.g., file deleted between check-out and check-in), this RuntimeException will propagate through the block execution stack. BlockManager.exceptionToCondition() does not map plain RuntimeException to any 4GL condition — it will be treated as an unhandled exception, likely surfacing as a STOP condition rather than an ERROR condition that ON ERROR phrases could handle.
sourceCharset can become null after deserializationStream.readExternal sets sourceCharset = javaSourceCS null ? null : Charset.forName(javaSourceCS). But the field's Javadoc says "This is never null".
I think "store" in RemoteStreamHelper should advance next stream id to be in sync with StreamDaemon.store().
#17 Updated by Constantin Asofiei 4 months ago
Hynek Cihlar wrote:
Code review 11271b.
FileStream.restoreStateAndOpenalways reopens withappend=true. What if the original stream is opened withappend=false?
Once the stream is opened, we are always appending - we can't reset its content.
The new
resolveIdmethod returns primitiveint, butwa.ids.get(stream)returnsIntegerwhich can benull. The original code inreverseLookupsafely handlednullviaInteger id = ids.get(s).
Fixed
Swapped Javadoc on serialization methods —
Stream.javaandFileStream.java
Fixed
Should access to
RemoteStreamHandler.remoteStreamsbe synchronized?
No, ServerResourceManager is context-local thus the proxy using RemoteStreamHandler is context-local
Double close in
WhenRemoteStreamHandler.invokefor file resource streamsstreamCloseis true and the stream is a local file resource, the code:
- Calls
local.checkOutStream(id)(closes local file handle)- Calls
remote.checkInStream(id, state)(opens on remote)- Calls
method.invoke(remote, args)(close on remote)- Then falls through to
method.invoke(handler, args)wherehandler local— executing close logic again on the already-checked-out local stream
Well, this close logic will also remove the stream from the various registries. That's why I use this order: checkout/checkin doesn't affect the registries, and calling close on both remote and local ensures both sides have cleared everything.
FileSystemDaemon.singletonused without null guard in static methods.
We have a case in deleteWorker where a JVM shutdown hook is executed - see #11325, but otherwise this must never be null.
RuntimeExceptionwrapping inrestoreStateAndOpen/saveStateAndClosebypasses 4GL error condition handling. Both methods catchIOException/ClassNotFoundExceptionand wrap them in rawRuntimeException(diff lines 377–385, 417–419). If a file cannot be re-opened during stream migration (e.g., file deleted between check-out and check-in), thisRuntimeExceptionwill propagate through the block execution stack.BlockManager.exceptionToCondition()does not map plainRuntimeExceptionto any 4GL condition — it will be treated as an unhandled exception, likely surfacing as a STOP condition rather than an ERROR condition thatON ERRORphrases could handle.
This must never happen. I'd rather abend if 'the OS did something with the file during switch' than treat this as a 4GL condition.
sourceCharset can become null after deserialization
Stream.readExternalsetssourceCharset = javaSourceCS null ? null : Charset.forName(javaSourceCS). But the field's Javadoc says "This is never null".
This is just a protection, it should never happen.
I think "store" in
RemoteStreamHelpershould advance next stream id to be in sync withStreamDaemon.store().
I need to double-check this.
#19 Updated by Teodor Gorghe 4 months ago
Constantin, do you need to do a change in 11271b? I am asking about this to know if I put this into ChUI regression testing.
#20 Updated by Constantin Asofiei 4 months ago
Teodor Gorghe wrote:
Constantin, do you need to do a change in 11271b? I am asking about this to know if I put this into ChUI regression testing.
Please go ahead and run ChUI.
#21 Updated by Constantin Asofiei 4 months ago
Hynek, you were correct to fix the 'nextId' on 'store'. Also I found a regression in ChUI - RemoteStream was caching state which was not set properly when opening a process stream. The fixes are in 11271b rev 16502 (after rebase).
I'll run ChUI, ETF and SXe again.
Greg: I think we need to plan for server-side redirected terminal (and thus frames) rather sooner than later; I think for batch processes, we can use this.
#23 Updated by Hynek Cihlar 4 months ago
Constantin Asofiei wrote:
Hynek, you were correct to fix the 'nextId' on 'store'.
Actually this one was spotted with AI ;-).
#24 Updated by Constantin Asofiei 4 months ago
- Status changed from Review to Test
Branch 11271b was merged to trunk rev 16508 and archived.
#25 Updated by Constantin Asofiei 4 months ago
Greg Shah wrote:
I think we need to plan for server-side redirected terminal (and thus frames) rather sooner than later; I think for batch processes, we can use this.
I agree. The question from #11271-14 remains: what is the expected effort for that?
There are not many static fields in the p2j.ui package remaining. After we clean this up and make them context-local, what we need to do is run a FWD client only on server-side; I would like to do this still via ClientDriver, which after authentication, if it sees server-side-resources=ALL, it switches automatically to this 'headless' mode (i.e. closes the socket, etc) and initializes ThinClient and other needed UI resources on server-side.
As for the effort: I think 3-4 weeks of work.
#26 Updated by Greg Shah 4 months ago
Constantin Asofiei wrote:
Greg Shah wrote:
I think we need to plan for server-side redirected terminal (and thus frames) rather sooner than later; I think for batch processes, we can use this.
I agree. The question from #11271-14 remains: what is the expected effort for that?
There are not many static fields in the
p2j.uipackage remaining. After we clean this up and make them context-local, what we need to do is run a FWD client only on server-side; I would like to do this still viaClientDriver, which after authentication, if it seesserver-side-resources=ALL, it switches automatically to this 'headless' mode (i.e. closes the socket, etc) and initializesThinClientand other needed UI resources on server-side.As for the effort: I think 3-4 weeks of work.
Please create a new task for this.
#27 Updated by Constantin Asofiei 3 months ago
- Related to Feature #11371: full server-side support of FWD clients added
#28 Updated by Constantin Asofiei 3 months ago
- If any context does not have ALL server-side resources, then register the network server in 'initializeServer', for filesystem and stream resources. Refs #11271 #11259
I'll merge this now. Affects multisession FWD mode.
#29 Updated by Constantin Asofiei 3 months ago
Branch 11271c was merged to trunk rev 16511 and archived.
#30 Updated by Hynek Cihlar 3 months ago
Greg Shah wrote:
Is the testcase from #11271-7 a common pattern? Should we just complete the implementation of a server-side headless/redirected UI?
Hynek: What is the work effort to accomplish that?
See #9055 and 9055a where the initial support for running client-side UI in server context was implemented. It included very limited use cases like MESSAGE statement.
For full UI I suppose the UI code will have to be cleaned from static Java fields and proper use of security context. A rough estimate 15 MDs.
#31 Updated by Constantin Asofiei 3 months ago
Branch 11271d fixes a conversion problem: FileSystemDaemon is used in conversion by .xpxg analysis (for OpenClient). This was merged to trunk rev 16516
#33 Updated by Alexandru Lungu 2 months ago
Constantin, is there any reason for:
if (wa.serverSideFs && !Paths.get(filename).isAbsolute())
{
if (filename.startsWith("." + getFileSeparator()))
{
filename = filename.substring(2);
}
filename = getWorkingDirectory().value.concat(getFileSeparator()).concat(filename);
}
inside FileSystemOps.searchPath? This class does:
lookupLegacyNamewhich should use relative names likemy/project/proc.p. It won't work with absolute path and/orgetWorkingDirectoryprefixed.ignoreClientSearchhonoring that, at least in another project, hasmy/project/proc.prelative paths.searchResourceJarsthat searches a file in a jar, but relative to the root of the jar. Also, appendinggetWorkingDirectorydoesn't help at all.if (filename.equalsIgnoreCase(quoterExecName))won't ever match.
So this code basically makes FileSystemOps.searchPath a no-op. Am I misunderstanding something?
#34 Updated by Constantin Asofiei 2 months ago
Alexandru Lungu wrote:
So this code basically makes
FileSystemOps.searchPatha no-op. Am I misunderstanding something?
Alex, you are correct; this needs to be moved lower in the method, after 4GL and jar lookup.
#35 Updated by Alexandru Lungu 2 months ago
I have some changes; I will commit them by EOD in 11242a where I tried to make filesystem,stream as a server-side resource work for the customer there.