From 0aa8fa407fe6b1cdb991cd1da272dc54edb2f53b Mon Sep 17 00:00:00 2001 From: Isaiah Haensel Date: Fri, 24 Jul 2026 10:14:53 +0800 Subject: [PATCH 1/2] Use common classloader for all RTI factory calls Adds a custom implementation of RtiFactoryFactory that ensures that all factory loading attempts use the same classloader, specifically the one that has been extended with the RTI HLA jar paths. --- .../disco/configuration/RprConfiguration.java | 4 +- .../disco/connection/rpr/RprConnection.java | 38 +++--- .../connection/rpr/RprRtiFactoryFactory.java | 117 ++++++++++++++++++ .../connection/rpr/types/EncoderFactory.java | 4 +- .../rpr/types/array/WrappedHlaFixedArray.java | 14 +-- .../types/array/WrappedHlaVariableArray.java | 8 +- .../types/fixed/WrappedHlaFixedRecord.java | 8 +- .../variant/WrappedHlaVariantRecord.java | 8 +- .../openlvc/disco/utils/ClassLoaderUtils.java | 4 + 9 files changed, 169 insertions(+), 36 deletions(-) create mode 100644 codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprRtiFactoryFactory.java diff --git a/codebase/src/java/disco/org/openlvc/disco/configuration/RprConfiguration.java b/codebase/src/java/disco/org/openlvc/disco/configuration/RprConfiguration.java index 97fdc9f0..8d852081 100644 --- a/codebase/src/java/disco/org/openlvc/disco/configuration/RprConfiguration.java +++ b/codebase/src/java/disco/org/openlvc/disco/configuration/RprConfiguration.java @@ -193,6 +193,8 @@ public void setRtiProvider( RtiProvider provider ) loadDefaultModules(); // re-add extension modules + // Doing this last means that extension modules don't + // respect overrides, which is intentional. registerExtensionModules( this.extensionModules.toArray(new String[0]) ); } } @@ -455,7 +457,7 @@ public File getFomOverridePath() ///////////////////////////////////////////////////////////////////////////////////////// /** * Get the list of all registered FOM modules. This is the union of the set of default - * modules plus any added through {@link #registerExtensionModules(File...)}. + * modules plus any added through {@link #registerExtensionModules}. *

* If you have a custom FOM Mapper, remember to make sure any required FOM Module is also * getting loaded. diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprConnection.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprConnection.java index 0edebd58..d02ad19e 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprConnection.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprConnection.java @@ -26,6 +26,7 @@ import java.util.Random; import java.util.stream.Stream; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; import org.openlvc.disco.DiscoException; import org.openlvc.disco.OpsCenter; @@ -60,7 +61,6 @@ import hla.rti1516e.ParameterHandleValueMap; import hla.rti1516e.RTIambassador; import hla.rti1516e.ResignAction; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.exceptions.FederateAlreadyExecutionMember; import hla.rti1516e.exceptions.FederateNameAlreadyInUse; import hla.rti1516e.exceptions.FederationExecutionAlreadyExists; @@ -355,7 +355,7 @@ private void initializeFederation() try { logger.debug( "Connecting to RTI" ); - this.rtiamb = RtiFactoryFactory.getRtiFactory().getRtiAmbassador(); + this.rtiamb = RprRtiFactoryFactory.getRtiFactory().getRtiAmbassador(); this.fedamb = new FederateAmbassador( this ); this.rtiamb.connect( this.fedamb, CallbackModel.HLA_IMMEDIATE, @@ -475,6 +475,7 @@ private void cleanupFederation() try { // Resign from the federation + logger.info( "Resigning from HLA federation" ); this.rtiamb.resignFederationExecution( ResignAction.DELETE_OBJECTS_THEN_DIVEST ); } catch( RTIexception rtie ) @@ -482,24 +483,31 @@ private void cleanupFederation() logger.warn( "Error while resigning from HLA federation: "+rtie.getMessage() ); } - // Delete the federation, to be a good citizen. Will get told off if people are - // still using it, so expect that. - try - { - this.rtiamb.destroyFederationExecution( rprConfiguration.getFederationName() ); - } - catch( RTIexception rtie ) + // Delete the federation, to be a good citizen (unless we're + // using the MAK RTI, which prefers to clean it up automatically + // after we disconnect). + // Will get told off if people are still using it, so expect that. + if( rprConfiguration.getRtiProvider() != RtiProvider.Mak ) { - // no-op - } - catch( Exception e ) - { - e.printStackTrace(); + try + { + logger.info( "Destroying federation execution" ); + this.rtiamb.destroyFederationExecution( rprConfiguration.getFederationName() ); + } + catch( RTIexception rtie ) + { + logger.catching( Level.WARN, rtie ); + } + catch( Exception e ) + { + logger.catching( e ); + } } // Disconnect from the RTI and then we are allll cleaned up try { + logger.info( "Disconnecting from RTI" ); this.rtiamb.disconnect(); this.rtiambConnected = false; } @@ -507,6 +515,8 @@ private void cleanupFederation() { throw new DiscoException( "Error disconnecting from RTI: "+rtie.getMessage(), rtie ); } + + logger.info( "Successfully disconnected from RTI" ); } diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprRtiFactoryFactory.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprRtiFactoryFactory.java new file mode 100644 index 00000000..b53a8ee4 --- /dev/null +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/RprRtiFactoryFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright 2015 Open LVC Project. + * + * This file is part of Open LVC Disco. + * + * 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. + */ +package org.openlvc.disco.connection.rpr; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.Set; + +import hla.rti1516e.RtiFactory; +import hla.rti1516e.exceptions.RTIinternalError; + +/** + * A custom implementation of {@link hla.rti1516e.RtiFactoryFactory} that ensures that the same + * classloader is used for all attempts to {@link hla.rti1516e.RtiFactory}. + *

+ * Note: the first invocation of a method from this class MUST occur on the same thread + * as the prior invocation of {@link org.openlvc.disco.utils.ClassLoaderUtils#extendClasspath}. + */ +public class RprRtiFactoryFactory +{ + //---------------------------------------------------------- + // STATIC VARIABLES + //---------------------------------------------------------- + private static ClassLoader classLoader; + + //---------------------------------------------------------- + // INSTANCE VARIABLES + //---------------------------------------------------------- + + //---------------------------------------------------------- + // CONSTRUCTORS + //---------------------------------------------------------- + public RprRtiFactoryFactory() {} + + //---------------------------------------------------------- + // INSTANCE METHODS + //---------------------------------------------------------- + + //========================================================================================== + //----------------------------- Accessor and Mutator Methods ------------------------------- + //========================================================================================== + + //---------------------------------------------------------- + // STATIC METHODS + //---------------------------------------------------------- + + public static RtiFactory getRtiFactory( String name ) throws RTIinternalError + { + for( RtiFactory rtiFactory : ServiceLoader.load(RtiFactory.class, getClassLoader()) ) + { + if( rtiFactory.rtiName().equals(name) ) + { + return rtiFactory; + } + } + + throw new RTIinternalError( "Cannot find factory matching "+name ); + } + + public static RtiFactory getRtiFactory() throws RTIinternalError + { + ServiceLoader loader = ServiceLoader.load( RtiFactory.class, getClassLoader() ); + Iterator iterator = loader.iterator(); + if( iterator.hasNext() ) + { + return iterator.next(); + } + else + { + throw new RTIinternalError( "Cannot find factory" ); + } + } + + public static Set getAvailableRtiFactories() + { + Set factories = new HashSet<>(); + + for( RtiFactory rtiFactory : ServiceLoader.load(RtiFactory.class, getClassLoader()) ) + { + factories.add( rtiFactory ); + } + + return factories; + } + + /** + * @return the current thread's classloader if possible, or {@code null} if the system + * classloader is to be used. + */ + private static ClassLoader getClassLoader() + { + if( classLoader == null ) + { + // prefer thread context classloader, since that'll usually + // be the one with the extended classpath + classLoader = Thread.currentThread().getContextClassLoader(); + } + + return classLoader; + } +} diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/EncoderFactory.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/EncoderFactory.java index 82e7388b..f5d43cd9 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/EncoderFactory.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/EncoderFactory.java @@ -18,8 +18,8 @@ package org.openlvc.disco.connection.rpr.types; import org.openlvc.disco.DiscoException; +import org.openlvc.disco.connection.rpr.RprRtiFactoryFactory; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.encoding.DataElement; import hla.rti1516e.encoding.DataElementFactory; import hla.rti1516e.encoding.HLAASCIIchar; @@ -305,7 +305,7 @@ private static final hla.rti1516e.encoding.EncoderFactory factory() { try { - RTI_FACTORY = RtiFactoryFactory.getRtiFactory().getEncoderFactory(); + RTI_FACTORY = RprRtiFactoryFactory.getRtiFactory().getEncoderFactory(); } catch( RTIinternalError e ) { diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaFixedArray.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaFixedArray.java index b42d6623..abbb2733 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaFixedArray.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaFixedArray.java @@ -20,8 +20,8 @@ import java.util.Iterator; import org.openlvc.disco.DiscoException; +import org.openlvc.disco.connection.rpr.RprRtiFactoryFactory; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.encoding.ByteWrapper; import hla.rti1516e.encoding.DataElement; import hla.rti1516e.encoding.DataElementFactory; @@ -52,9 +52,9 @@ public WrappedHlaFixedArray( T... values ) try { - this.internal = RtiFactoryFactory.getRtiFactory() - .getEncoderFactory() - .createHLAfixedArray( values ); + this.internal = RprRtiFactoryFactory.getRtiFactory() + .getEncoderFactory() + .createHLAfixedArray( values ); } catch( RTIinternalError e ) { @@ -68,9 +68,9 @@ public WrappedHlaFixedArray( DataElementFactory factory, int size ) try { - this.internal = RtiFactoryFactory.getRtiFactory() - .getEncoderFactory() - .createHLAfixedArray( factory, size ); + this.internal = RprRtiFactoryFactory.getRtiFactory() + .getEncoderFactory() + .createHLAfixedArray( factory, size ); } catch( RTIinternalError e ) { diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaVariableArray.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaVariableArray.java index 18f8fa55..0f2804fc 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaVariableArray.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/array/WrappedHlaVariableArray.java @@ -20,8 +20,8 @@ import java.util.Iterator; import org.openlvc.disco.DiscoException; +import org.openlvc.disco.connection.rpr.RprRtiFactoryFactory; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.encoding.ByteWrapper; import hla.rti1516e.encoding.DataElement; import hla.rti1516e.encoding.DataElementFactory; @@ -52,9 +52,9 @@ public WrappedHlaVariableArray( DataElementFactory factory, T... values ) try { - this.internal = RtiFactoryFactory.getRtiFactory() - .getEncoderFactory() - .createHLAvariableArray( factory, values ); + this.internal = RprRtiFactoryFactory.getRtiFactory() + .getEncoderFactory() + .createHLAvariableArray( factory, values ); } catch( RTIinternalError e ) { diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/fixed/WrappedHlaFixedRecord.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/fixed/WrappedHlaFixedRecord.java index 4cc769a6..b98d5271 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/fixed/WrappedHlaFixedRecord.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/fixed/WrappedHlaFixedRecord.java @@ -20,8 +20,8 @@ import java.util.Iterator; import org.openlvc.disco.DiscoException; +import org.openlvc.disco.connection.rpr.RprRtiFactoryFactory; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.encoding.ByteWrapper; import hla.rti1516e.encoding.DataElement; import hla.rti1516e.encoding.DecoderException; @@ -50,9 +50,9 @@ public WrappedHlaFixedRecord() try { - this.internal = RtiFactoryFactory.getRtiFactory() - .getEncoderFactory() - .createHLAfixedRecord(); + this.internal = RprRtiFactoryFactory.getRtiFactory() + .getEncoderFactory() + .createHLAfixedRecord(); } catch( RTIinternalError e ) { diff --git a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/variant/WrappedHlaVariantRecord.java b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/variant/WrappedHlaVariantRecord.java index 9e052571..96dd3e7b 100644 --- a/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/variant/WrappedHlaVariantRecord.java +++ b/codebase/src/java/disco/org/openlvc/disco/connection/rpr/types/variant/WrappedHlaVariantRecord.java @@ -18,10 +18,10 @@ package org.openlvc.disco.connection.rpr.types.variant; import org.openlvc.disco.DiscoException; +import org.openlvc.disco.connection.rpr.RprRtiFactoryFactory; import org.openlvc.disco.connection.rpr.types.enumerated.EnumHolder; import org.openlvc.disco.connection.rpr.types.enumerated.ExtendedDataElement; -import hla.rti1516e.RtiFactoryFactory; import hla.rti1516e.encoding.ByteWrapper; import hla.rti1516e.encoding.DataElement; import hla.rti1516e.encoding.DecoderException; @@ -50,9 +50,9 @@ public WrappedHlaVariantRecord( T discriminant ) try { - this.internal = RtiFactoryFactory.getRtiFactory() - .getEncoderFactory() - .createHLAvariantRecord( new EnumHolder<>(discriminant) ); + this.internal = RprRtiFactoryFactory.getRtiFactory() + .getEncoderFactory() + .createHLAvariantRecord( new EnumHolder<>(discriminant) ); } catch( RTIinternalError e ) { diff --git a/codebase/src/java/disco/org/openlvc/disco/utils/ClassLoaderUtils.java b/codebase/src/java/disco/org/openlvc/disco/utils/ClassLoaderUtils.java index 9753745a..e4c9c35a 100644 --- a/codebase/src/java/disco/org/openlvc/disco/utils/ClassLoaderUtils.java +++ b/codebase/src/java/disco/org/openlvc/disco/utils/ClassLoaderUtils.java @@ -55,6 +55,10 @@ public class ClassLoaderUtils /** * Add a given path or set of paths to a custom classloader, and sets that as the ClassLoader * for the current Thread + *

+ * Note: because we're altering the current thread's classloader, this is only useful + * if performed on the same thread as the first call to a method of + * {@link org.openlvc.disco.connection.rpr.RprRtiFactoryFactory}. * * @param paths The paths to add to the lookup set * @throws DiscoException If there any invalid files are provided From 8f1900e1610b05336f2f9b19ef97b93561347803 Mon Sep 17 00:00:00 2001 From: Isaiah Haensel Date: Mon, 27 Jul 2026 11:26:59 +0800 Subject: [PATCH 2/2] Bump build number to 1.2.0.8 --- codebase/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codebase/build.properties b/codebase/build.properties index 1854a2b0..5699c932 100644 --- a/codebase/build.properties +++ b/codebase/build.properties @@ -23,7 +23,7 @@ build.longname = Open LVC Disco build.shortname = disco build.version = 1.2.0 -build.number = 7 +build.number = 8 ################################# # Java Development Kit Settings #