From 7fefad9f960f72b6347cf3ffe40cc0dfa795c148 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 26 Dec 2024 09:23:42 -0600 Subject: [PATCH] HHH-18937 - Introduce a JavaServiceLoadable extension to contribute "strategy selectors" --- .../internal/StrategySelectorImpl.java | 232 +++++++++++------- .../spi/NamedStrategyContributions.java | 36 +++ .../spi/NamedStrategyContributor.java | 30 +++ .../selector/spi/StrategySelector.java | 53 ++-- ...NamedStrategyContributorBaselineTests.java | 21 ++ .../NamedStrategyContributorImpl.java | 30 +++ .../NamedStrategyContributorTests.java | 69 ++++++ .../registry/strategy/StrategyContract.java | 13 + .../strategy/StrategyContractImpl.java | 11 + 9 files changed, 380 insertions(+), 115 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributions.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributor.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorBaselineTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContract.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContractImpl.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java index ae7767f317e2..dbf8de8f16ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java @@ -19,38 +19,28 @@ import org.hibernate.boot.registry.selector.spi.StrategySelectionException; import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.boot.registry.selector.spi.NamedStrategyContributions; +import org.hibernate.boot.registry.selector.spi.NamedStrategyContributor; import org.jboss.logging.Logger; /** * Standard implementation of the {@link StrategySelector} contract. * + * @implNote Supports both {@linkplain #namedStrategyImplementorByStrategyMap normal} + * and {@linkplain #lazyStrategyImplementorByStrategyMap lazy} registration. + * * @author Steve Ebersole */ public class StrategySelectorImpl implements StrategySelector { - private static final Logger log = Logger.getLogger( StrategySelectorImpl.class ); - private static final StrategyCreator STANDARD_STRATEGY_CREATOR = strategyClass -> { - try { - return strategyClass.newInstance(); - } - catch (Exception e) { - throw new StrategySelectionException( - String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ), - e - ); - } - }; - - //Map based approach: most suited for explicit registrations from integrators - private final Map> namedStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); + private static final StrategyCreator STANDARD_STRATEGY_CREATOR = StrategySelectorImpl::create; - //"Lazy" approach: more efficient as we aim to not initialize all implementation classes; - //this is preferable for internal services such as Dialect, as we have a significant amount of them, making - //it worthwhile to try be a bit more efficient about them. - private final Map lazyStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); + private final Map,Map>> namedStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); + private final Map, LazyServiceResolver> lazyStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); private final ClassLoaderService classLoaderService; + private final Collection contributors; /** * Constructs a StrategySelectorImpl using the given class loader service. @@ -59,88 +49,29 @@ public class StrategySelectorImpl implements StrategySelector { */ public StrategySelectorImpl(ClassLoaderService classLoaderService) { this.classLoaderService = classLoaderService; - } - - public void registerStrategyLazily(Class strategy, LazyServiceResolver resolver) { - LazyServiceResolver previous = lazyStrategyImplementorByStrategyMap.put( strategy, resolver ); - if ( previous != null ) { - throw new HibernateException( "Detected a second LazyServiceResolver replacing an existing LazyServiceResolver implementation for strategy " + strategy.getName() ); - } - } - - @Override - public void registerStrategyImplementor(Class strategy, String name, Class implementation) { - final Map namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.computeIfAbsent( - strategy, - aClass -> new ConcurrentHashMap<>() - ); - - final Class old = namedStrategyImplementorMap.put( name, implementation ); - if ( old == null ) { - if ( log.isTraceEnabled() ) { - log.trace( - String.format( - "Registering named strategy selector [%s] : [%s] -> [%s]", - strategy.getName(), - name, - implementation.getName() - ) - ); - } - } - else { - if ( log.isDebugEnabled() ) { - log.debug( - String.format( - "Registering named strategy selector [%s] : [%s] -> [%s] (replacing [%s])", - strategy.getName(), - name, - implementation.getName(), - old.getName() - ) - ); - } - } - } - - @Override - public void unRegisterStrategyImplementor(Class strategy, Class implementation) { - final Map namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.get( strategy ); - if ( namedStrategyImplementorMap == null ) { - log.debug( "Named strategy map did not exist on call to un-register" ); - return; - } - - final Iterator itr = namedStrategyImplementorMap.values().iterator(); - while ( itr.hasNext() ) { - final Class registered = (Class) itr.next(); - if ( registered.equals( implementation ) ) { - itr.remove(); - } - } - // try to clean up after ourselves... - if ( namedStrategyImplementorMap.isEmpty() ) { - namedStrategyImplementorByStrategyMap.remove( strategy ); + this.contributors = classLoaderService.loadJavaServices( NamedStrategyContributor.class ); + for ( NamedStrategyContributor contributor : contributors ) { + contributor.contributeStrategyImplementations( new StartupContributions() ); } } @Override @SuppressWarnings("unchecked") public Class selectStrategyImplementor(Class strategy, String name) { - final Map namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.get( strategy ); + final Map> namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.get( strategy ); if ( namedStrategyImplementorMap != null ) { - final Class registered = namedStrategyImplementorMap.get( name ); + final Class registered = namedStrategyImplementorMap.get( name ); if ( registered != null ) { return (Class) registered; } } - LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); + final LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); if ( lazyServiceResolver != null ) { - Class resolve = lazyServiceResolver.resolve( name ); + final Class resolve = lazyServiceResolver.resolve( name ); if ( resolve != null ) { - return resolve; + return (Class) resolve; } } @@ -175,7 +106,7 @@ public T resolveDefaultableStrategy( Class strategy, Object strategyReference, Callable defaultResolver) { - return (T) resolveStrategy( strategy, strategyReference, defaultResolver, STANDARD_STRATEGY_CREATOR ); + return (T) resolveStrategy( strategy, strategyReference, defaultResolver, (StrategyCreator) STANDARD_STRATEGY_CREATOR ); } @Override @@ -193,17 +124,17 @@ public T resolveStrategy( } @Override - @SuppressWarnings("unchecked") - public Collection getRegisteredStrategyImplementors(Class strategy) { - LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); + public Collection> getRegisteredStrategyImplementors(Class strategy) { + final LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); if ( lazyServiceResolver != null ) { throw new StrategySelectionException( "Can't use this method on for strategy types which are embedded in the core library" ); } - final Map registrations = namedStrategyImplementorByStrategyMap.get( strategy ); + final Map> registrations = namedStrategyImplementorByStrategyMap.get( strategy ); if ( registrations == null ) { return Collections.emptySet(); } - return new HashSet( registrations.values() ); + //noinspection unchecked,rawtypes + return (Collection) new HashSet<>( registrations.values() ); } @SuppressWarnings("unchecked") @@ -244,4 +175,121 @@ public T resolveStrategy( ); } } + + private static T create(Class strategyClass) { + try { + return strategyClass.getDeclaredConstructor().newInstance(); + } + catch (Exception e) { + throw new StrategySelectionException( + String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ), + e + ); + } + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Lifecycle + + public void registerStrategyLazily(Class strategy, LazyServiceResolver resolver) { + LazyServiceResolver previous = lazyStrategyImplementorByStrategyMap.put( strategy, resolver ); + if ( previous != null ) { + throw new HibernateException( "Detected a second LazyServiceResolver replacing an existing LazyServiceResolver implementation for strategy " + strategy.getName() ); + } + } + + private void contributeImplementation(Class strategy, Class implementation, String... names) { + final Map> namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.computeIfAbsent( + strategy, + aClass -> new ConcurrentHashMap<>() + ); + + for ( int i = 0; i < names.length; i++ ) { + final String name = names[i]; + + final Class old = namedStrategyImplementorMap.put( name, implementation ); + if ( old == null ) { + if ( log.isTraceEnabled() ) { + log.trace( + String.format( + "Registering named strategy selector [%s] : [%s] -> [%s]", + strategy.getName(), + name, + implementation.getName() + ) + ); + } + } + else { + if ( log.isDebugEnabled() ) { + log.debug( + String.format( + "Registering named strategy selector [%s] : [%s] -> [%s] (replacing [%s])", + strategy.getName(), + name, + implementation.getName(), + old.getName() + ) + ); + } + } + } + } + + private void removeImplementation(Class strategy, Class implementation) { + final Map> namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.get( strategy ); + if ( namedStrategyImplementorMap == null ) { + log.debug( "Named strategy map did not exist on call to un-register" ); + return; + } + + final Iterator> itr = namedStrategyImplementorMap.values().iterator(); + while ( itr.hasNext() ) { + final Class registered = itr.next(); + if ( registered.equals( implementation ) ) { + itr.remove(); + } + } + + // try to clean up after ourselves... + if ( namedStrategyImplementorMap.isEmpty() ) { + namedStrategyImplementorByStrategyMap.remove( strategy ); + } + } + + @Override + public void stop() { + for ( NamedStrategyContributor contributor : contributors ) { + contributor.clearStrategyImplementations( new ShutdownContributions() ); + } + } + + private class StartupContributions implements NamedStrategyContributions { + @Override + public void contributeStrategyImplementor(Class strategy, Class implementation, String... names) { + contributeImplementation( strategy, implementation, names ); + } + + @Override + public void removeStrategyImplementor(Class strategy, Class implementation) { + removeImplementation( strategy, implementation ); + } + } + + private class ShutdownContributions extends StartupContributions { + @Override + public void contributeStrategyImplementor(Class strategy, Class implementation, String... names) { + throw new IllegalStateException( "Should not register strategies during shutdown" ); + } + } + + @Override + public void registerStrategyImplementor(Class strategy, String name, Class implementation) { + contributeImplementation( strategy, implementation, name ); + } + + @Override + public void unRegisterStrategyImplementor(Class strategy, Class implementation) { + removeImplementation( strategy, implementation ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributions.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributions.java new file mode 100644 index 000000000000..d3382d052b8b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributions.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.boot.registry.selector.spi; + +/** + * Target for {@linkplain NamedStrategyContributor} + * + * @see StrategySelector + * + * @author Steve Ebersole + */ +public interface NamedStrategyContributions { + /** + * Registers a named implementor of a particular strategy contract. + * + * @param strategy The strategy contract. + * @param implementation The implementation class. + * @param names The registration names. + * + * @param The strategy type. + */ + void contributeStrategyImplementor(Class strategy, Class implementation, String... names); + + /** + * Un-registers a named implementor of a particular strategy contract. Un-registers all named registrations + * for the given strategy contract naming the given class. + * + * @param strategy The strategy contract. + * @param implementation The implementation class. + * + * @param The strategy type. + */ + void removeStrategyImplementor(Class strategy, Class implementation); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributor.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributor.java new file mode 100644 index 000000000000..44ef6bd152e4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/NamedStrategyContributor.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.boot.registry.selector.spi; + +import org.hibernate.service.JavaServiceLoadable; + +/** + * Discoverable contributor for named references which are resolvable via StrategySelector. + * + * @see StrategySelector + * @see NamedStrategyContributions + * + * @author Steve Ebersole + */ +@JavaServiceLoadable +public interface NamedStrategyContributor { + /** + * Allows registration of named strategy implementations. + * Called on bootstrap. + */ + void contributeStrategyImplementations(NamedStrategyContributions contributions); + + /** + * Allows cleanup of (presumably previously {@linkplain #contributeStrategyImplementations registered}) strategy implementations. + * Called on shutdown. + */ + void clearStrategyImplementations(NamedStrategyContributions contributions); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategySelector.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategySelector.java index 4e6604c8bc6e..731cd6d91935 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategySelector.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategySelector.java @@ -10,6 +10,7 @@ import org.hibernate.service.Service; import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.service.spi.Stoppable; /** * Service which acts as a registry for named strategy implementations. @@ -33,29 +34,7 @@ * * @author Steve Ebersole */ -public interface StrategySelector extends Service { - /** - * Registers a named implementor of a particular strategy contract. - * - * @param strategy The strategy contract. - * @param name The registration name - * @param implementation The implementation Class - * @param The type of the strategy. Used to make sure that the strategy and implementation are type - * compatible. - */ - void registerStrategyImplementor(Class strategy, String name, Class implementation); - - /** - * Un-registers a named implementor of a particular strategy contract. Un-registers all named registrations - * for the given strategy contract naming the given class. - * - * @param strategy The strategy contract. - * @param implementation The implementation Class - * @param The type of the strategy. Used to make sure that the strategy and implementation are type - * compatible. - */ - void unRegisterStrategyImplementor(Class strategy, Class implementation); - +public interface StrategySelector extends Service, Stoppable { /** * Locate the named strategy implementation. * @@ -149,4 +128,32 @@ public interface StrategySelector extends Service { * @return The implementors. Should never return {@code null} */ Collection> getRegisteredStrategyImplementors(Class strategy); + + /** + * Registers a named implementor of a particular strategy contract. + * + * @param strategy The strategy contract. + * @param name The registration name + * @param implementation The implementation class + * + * @param The strategy type. + * + * @deprecated Use {@linkplain NamedStrategyContributor} instead + */ + @Deprecated( since = "7.0", forRemoval = true ) + void registerStrategyImplementor(Class strategy, String name, Class implementation); + + /** + * Un-registers a named implementor of a particular strategy contract. Un-registers all named registrations + * for the given strategy contract naming the given class. + * + * @param strategy The strategy contract. + * @param implementation The implementation class + * + * @param The strategy type. + * + * @deprecated Use {@linkplain NamedStrategyContributor} instead + */ + @Deprecated( since = "7.0", forRemoval = true ) + void unRegisterStrategyImplementor(Class strategy, Class implementation); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorBaselineTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorBaselineTests.java new file mode 100644 index 000000000000..00db03d217b9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorBaselineTests.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.boot.registry.strategy; + +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@BootstrapServiceRegistry +public class NamedStrategyContributorBaselineTests { + @Test + void testWithNoContributions(ServiceRegistryScope registryScope) { + NamedStrategyContributorTests.check( registryScope, false ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorImpl.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorImpl.java new file mode 100644 index 000000000000..ca793f38386c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorImpl.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.boot.registry.strategy; + +import org.hibernate.boot.registry.selector.spi.NamedStrategyContributions; +import org.hibernate.boot.registry.selector.spi.NamedStrategyContributor; + +/** + * @author Steve Ebersole + */ +public class NamedStrategyContributorImpl implements NamedStrategyContributor { + @Override + public void contributeStrategyImplementations(NamedStrategyContributions contributions) { + contributions.contributeStrategyImplementor( + StrategyContract.class, + StrategyContractImpl.class, + StrategyContractImpl.class.getName(), + StrategyContractImpl.class.getSimpleName(), + "main", + "alternate" + ); + } + + @Override + public void clearStrategyImplementations(NamedStrategyContributions contributions) { + contributions.removeStrategyImplementor( StrategyContract.class, StrategyContractImpl.class ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorTests.java new file mode 100644 index 000000000000..03578310e4f0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/NamedStrategyContributorTests.java @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.boot.registry.strategy; + +import org.hibernate.boot.registry.selector.spi.NamedStrategyContributor; +import org.hibernate.boot.registry.selector.spi.StrategySelectionException; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@BootstrapServiceRegistry( javaServices = @BootstrapServiceRegistry.JavaService( + role = NamedStrategyContributor.class, + impl = NamedStrategyContributorImpl.class +) ) +public class NamedStrategyContributorTests { + @Test + void testWithContributions(ServiceRegistryScope registryScope) { + check( registryScope, true ); + } + +// @Test +// @BootstrapServiceRegistry +// @ServiceRegistry +// void testWithNoContributions(ServiceRegistryScope registryScope) { +// check( registryScope, false ); +// } + + public static void check(ServiceRegistryScope registryScope, boolean javaServiceApplied) { + registryScope.withService( StrategySelector.class, (selector) -> { + // null ref + checkExistence( selector, null, false ); + // resolvable because we pass the FQN + checkExistence( selector, StrategyContractImpl.class.getName(), true ); + + checkExistence( selector, StrategyContractImpl.class.getSimpleName(), javaServiceApplied ); + checkExistence( selector, "main", javaServiceApplied ); + checkExistence( selector, "alternate", javaServiceApplied ); + checkExistence( selector, "non-existent", false ); + } ); + } + + private static void checkExistence(StrategySelector selector, Object ref, boolean expectToExist) { + try { + final StrategyContract resolved = selector.resolveDefaultableStrategy( StrategyContract .class, ref, (StrategyContract ) null ); + if ( expectToExist ) { + assertThat( resolved ).isNotNull(); + } + else { + // generally this is the ref == null case + assertThat( resolved ).isNull(); + } + } + catch (StrategySelectionException e) { + if ( expectToExist ) { + throw e; + } + // otherwise, this is expected + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContract.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContract.java new file mode 100644 index 000000000000..651aaf897447 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContract.java @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.boot.registry.strategy; + +/** + * Name-selectable strategy + * + * @author Steve Ebersole + */ +public interface StrategyContract { +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContractImpl.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContractImpl.java new file mode 100644 index 000000000000..f70c708e963f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/registry/strategy/StrategyContractImpl.java @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.boot.registry.strategy; + +/** + * @author Steve Ebersole + */ +public class StrategyContractImpl implements StrategyContract { +}