From da9be865be11dc210a1b042a798dced73f903224 Mon Sep 17 00:00:00 2001 From: Peter Keller Date: Mon, 22 Sep 2025 11:49:05 +0100 Subject: [PATCH 1/2] Prep for upcoming changes to the Abstract Beamline Interface - In the very near future, there will be two changes on the Java side: - Package names in the ABI will change - ABI types and Py4jMessage will be imported into the default JVM view on the Java side instead of the Python side; Python code should use the unqualified class/interface names - This commit makes the Python code that interacts with the GPhL workflow insensitive to those changes --- .../Gphl/GphlWorkflowConnection.py | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py index fbb3de8ecc..bb3094080f 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py @@ -96,7 +96,7 @@ def __init__(self, name): super().__init__(name) # Py4J gateway to external workflow program self._gateway = None - self.msg_class_imported = False + self.jvm_imports_checked = False # ID for current workflow calculation self._enactment_id = None @@ -221,7 +221,7 @@ def start_workflow(self, workflow_queue, workflow_model_obj): # so we are never in state 'ON'/STANDBY raise RuntimeError("Workflow is already running, cannot be started") - self.msg_class_imported = False + self.jvm_imports_checked = False # Cannot be done in init, where the api.sessions link is not yet ready self.config.software_paths["GPHL_WDIR"] = os.path.join( @@ -479,30 +479,30 @@ def processMessage(self, py4j_message): if self.get_state() is self.STATES.OFF: return None - if not self.msg_class_imported: - # Guard agaiunst Java classes being moved to different packages - try: - msg_class = ( - self._gateway.jvm.py4j.reflection.ReflectionUtil.classForName( - "co.gphl.sdcp.astra.service.py4j.Py4jMessage" - ) - ) - java_gateway.java_import( - self._gateway.jvm, "co.gphl.sdcp.astra.service.py4j.Py4jMessage" - ) - except Py4JJavaError: - msg_class = ( - self._gateway.jvm.py4j.reflection.ReflectionUtil.classForName( - "co.gphl.sdcp.py4j.Py4jMessage" - ) - ) - java_gateway.java_import( - self._gateway.jvm, "co.gphl.sdcp.py4j.Py4jMessage" - ) - logging.getLogger("HWR").debug( - "GΦL workflow Py4jMessage class is: %s" % msg_class - ) - self.msg_class_imported = True + if not self.jvm_imports_checked: + + # We need to use dir to check for the presence or absence of an imported class. hasattr/getattr don't do + # what is needed here, because if the attribute name is not present java_gateway receives + # proto.SUCCESS_PACKAGE from the Java side and instantiates a JavaPackage with the name, even though + # no such package exists in the JVM. See: + # https://github.com/py4j/py4j/blob/cb9e392d8fc5bec6b99a612e2911017900061628/py4j-python/src/py4j/java_gateway.py#L1748 + # This looks like a py4j bug on the Java side but needs more investigation. + + # The Py4jMessage class is used as the indicator class here: we assume that if it has been imported, + # all other unqualified Java classnames used via the default JVM view have been imported, otherwise + # none of them have been imported and we need to do it here. + if "Py4jMessage" not in dir(self._gateway.jvm): + for qualified_class_name in ("co.gphl.sdcp.astra.service.py4j.Py4jMessage", + "co.gphl.beamline.v2_unstable.instrumentation.CentringStatus", + "co.gphl.beamline.v2_unstable.domain_types.CrystalClass", + "co.gphl.beamline.v2_unstable.domain_types.ChemicalElement", + "co.gphl.beamline.v2_unstable.domain_types.AbsorptionEdge", + ""): + java_gateway.java_import(self._gateway.jvm, qualified_class_name) + logging.getLogger("HWR").warn("Importing required unqualified class names from the JVM explicitly") + logging.getLogger("HWR").warn("Please consider upgrading the GPhL workflow application") + + self.jvm_imports_checked = True xx0 = self._decode_py4j_message(py4j_message) message_type = xx0.message_type @@ -923,7 +923,7 @@ def _response_to_server(self, payload, correlation_id): py4j_payload = self._payload_to_java(payload) try: - response = self._gateway.jvm.co.gphl.sdcp.astra.service.py4j.Py4jMessage( + response = self._gateway.jvm.Py4jMessage( py4j_payload, correlation_id ) except: @@ -938,7 +938,7 @@ def _response_to_server(self, payload, correlation_id): def _CentringDone_to_java(self, centringDone): jvm = self._gateway.jvm return jvm.astra.messagebus.messages.information.CentringDoneImpl( - jvm.co.gphl.beamline.v2_unstable.instrumentation.CentringStatus.valueOf( + jvm.CentringStatus.valueOf( centringDone.status ), self.to_java_time(centringDone.timestamp), @@ -1042,7 +1042,7 @@ def _SelectedLattice_to_java(self, selectedLattice): crystal_classes = selectedLattice.userCrystalClasses if crystal_classes: ccset = set( - jvm.co.gphl.beamline.v2_unstable.domain_types.CrystalClass.fromStringList( + jvm.CrystalClass.fromStringList( self.toJStringArray(crystal_classes) ) ) @@ -1093,7 +1093,7 @@ def _UserProvidedInfo_to_java(self, userProvidedInfo): crystal_classes = userProvidedInfo.crystalClasses if crystal_classes: ccset = set( - jvm.co.gphl.beamline.v2_unstable.domain_types.CrystalClass.fromStringList( + jvm.CrystalClass.fromStringList( self.toJStringArray(crystal_classes) ) ) @@ -1122,10 +1122,10 @@ def _AnomalousScatterer_to_java(self, anomalousScatterer): if anomalousScatterer is None: return None - element = jvm.co.gphl.beamline.v2_unstable.domain_types.ChemicalElement.valueOf( + element = jvm.ChemicalElement.valueOf( anomalousScatterer.element ) - edge = jvm.co.gphl.beamline.v2_unstable.domain_types.AbsorptionEdge.valueOf( + edge = jvm.AbsorptionEdge.valueOf( anomalousScatterer.edge ) return jvm.astra.messagebus.messages.domain_types.AnomalousScattererImpl( From 0fdca95be3b10e46b2bae17a9d8137c9fe86bd2a Mon Sep 17 00:00:00 2001 From: Peter Keller Date: Mon, 22 Sep 2025 12:06:09 +0100 Subject: [PATCH 2/2] fixup! Prep for upcoming changes to the Abstract Beamline Interface - Remove cruft that got into the code by accident --- mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py index bb3094080f..dbc5976e47 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py @@ -496,8 +496,7 @@ def processMessage(self, py4j_message): "co.gphl.beamline.v2_unstable.instrumentation.CentringStatus", "co.gphl.beamline.v2_unstable.domain_types.CrystalClass", "co.gphl.beamline.v2_unstable.domain_types.ChemicalElement", - "co.gphl.beamline.v2_unstable.domain_types.AbsorptionEdge", - ""): + "co.gphl.beamline.v2_unstable.domain_types.AbsorptionEdge"): java_gateway.java_import(self._gateway.jvm, qualified_class_name) logging.getLogger("HWR").warn("Importing required unqualified class names from the JVM explicitly") logging.getLogger("HWR").warn("Please consider upgrading the GPhL workflow application")