I apologize for attempting this monstrosity, but... It is forced on me.
I have a .jar implementing logic that I receive prebuilt. This .jar exports some API you can use (... in Java...) to request information about file-system items you hand in as paths.
This .jar is compatible with Java 11, and runs with the "Zulu" Java VM.
I need to "wrap" it in some way that will behave like a global daemon, launched and managed by launchd, available to other components (apps, user-agents and global-daemons) in our product.
Running it as a global daemon is a breeze - simply place a .plist in /Library/LaunchDaemons/myMonster.plist and have the java -jar <path to my .jar> arguments, throw in a 'keep alive' option, and it runs.
Now... It makes sense for other components to pass it "queries" and receive "results" via XPC connection. First, because this way I could deny connection to unknown components, I could have a secure connection, and also integrate nicely from the other components ObjC code.
However... XPC isn't something available in JDK, and the actual executable launched is the Zulu java command binary of course, that I can't modify.
So... I tried to use JNA (Java Native Access) and (with much tears and sweat) get my java code to create an xpc connection (as client! not "service") to another XPC Service already running.
Also, I was only able to do it via xpc.h (the old C API. not ObjC NSXPCConnection as of yet).
The documentation on old C-style XPC Services is very thin, almost nothing... and the only thing about Creating an XPC Service of this style says:
// If you are writing a low-level (C-based) XPC service, implement a minimal main function to register your event handler, as shown in the following code listing. Replace my_event_handler with the name of your event handler function.
int main(int argc, const char *argv[]) {
xpc_main(my_event_handler);
// The xpc_main() function never returns.
exit(EXIT_FAILURE);
}
Which of course, I can't do! I don't control the process 'main()' entry point (it's java...) and I can't block anything.
So here's my question:
- Where in the lifecycle of a Java program can I call on my native code to set up The XPC Service?
- Is there a non-blocking alternative to xpc_main(my_event_handler) I can use to start my service?
I know this calls for multi-disciplinary knowledge but any hint or idea will be greatly appreciated.
Thanks!
I apologize for attempting this monstrosity
Trust me, I’ve seen worse (-:
Where in the lifecycle of a Java program can I call on my native code to set up the XPC Service?
You are mixing up XPC Service and XPC service (-: Yeah, that’s confusing. Let me explain:
-
An XPC Service is bundle that contains an executable that’s managed by
launchd
. It vends services to clients as an XPC service. -
An XPC service is a Mach service that speaks the XPC protocol.
Note This is a convention that I use, not something that’s well-defined by Apple. One day I’ll get this sorted out in the official documentation, but today is not that day )-:
When you set a MachServices
property is a launchd
property list, you’re creating a launchd
daemon that advertises an XPC service but it is not an XPC Service; it’s still a launchd
daemon. Given that, it is not allowed to call xpc_main
.
A dedicated macOS daemon would instead call dispatch_main
. In your case, however, your third-party runtime is using some other main event loop (based on select
, or kqueues, or whatever) and that’s fine. A launchd
daemon has that flexibility.
Is there a non-blocking alternative to
xpc_main(my_event_handler)
I can use to start my service?
No, because that doesn’t make sense in this context. Rather, your XPC subsystem should ‘check in’ with launchd
using a completely different mechanism, that is, xpc_connection_create_mach_service
with the XPC_CONNECTION_MACH_SERVICE_LISTENER
option. That returns a listener connection, configured to listen on that Mach service name. When a client creates a connection to that listener, your event handler is called as discussed in the doc comments for xpc_connection_set_event_handler
.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"