11package ai .timefold .solver .core .enterprise ;
22
33import java .lang .reflect .InvocationTargetException ;
4- import java .lang .reflect .Method ;
54import java .util .function .BiFunction ;
65import java .util .function .Function ;
76import java .util .function .Supplier ;
4241
4342public interface TimefoldSolverEnterpriseService {
4443
44+ final class InstanceCarrier {
45+
46+ // Workaround to be able to have a lazy singleton inside of an interface.
47+ // Otherwise load() would be calling the reflective constructor every time, which is expensive.
48+ private static TimefoldSolverEnterpriseService INSTANCE ;
49+
50+ }
51+
4552 String SOLVER_NAME = "Timefold Solver" ;
4653 String COMMUNITY_NAME = "Community Edition" ;
4754 String COMMUNITY_COORDINATES = "ai.timefold.solver:timefold-solver-core" ;
@@ -62,14 +69,18 @@ static String identifySolverVersion() {
6269 }
6370
6471 @ SuppressWarnings ("unchecked" )
65- static TimefoldSolverEnterpriseService load ()
66- throws ClassNotFoundException , NoSuchMethodException , InvocationTargetException , IllegalAccessException {
72+ static TimefoldSolverEnterpriseService load () throws ClassNotFoundException , NoSuchMethodException ,
73+ InvocationTargetException , IllegalAccessException , InstantiationException {
74+ if (InstanceCarrier .INSTANCE != null ) {
75+ return InstanceCarrier .INSTANCE ;
76+ }
6777 // Avoids ServiceLoader by using reflection directly.
6878 var clz = (Class <? extends TimefoldSolverEnterpriseService >) Class
6979 .forName ("ai.timefold.solver.enterprise.core.DefaultTimefoldSolverEnterpriseService" );
70- Method method = clz .getMethod ("getInstance" , Function .class );
71- return (TimefoldSolverEnterpriseService ) method .invoke (null ,
72- (Function <Class <?>, String >) TimefoldSolverEnterpriseService ::getVersionString );
80+ var ctor = clz .getDeclaredConstructor (Function .class );
81+ InstanceCarrier .INSTANCE =
82+ ctor .newInstance ((Function <Class <?>, String >) TimefoldSolverEnterpriseService ::getVersionString );
83+ return InstanceCarrier .INSTANCE ;
7384 }
7485
7586 static String getVersionString (Class <?> clz ) {
@@ -84,17 +95,15 @@ static TimefoldSolverEnterpriseService loadOrFail(Feature feature) {
8495 throw new IllegalStateException ("""
8596 No valid Timefold Enterprise License was found.
8697 Please contact Timefold to obtain a valid license,
87- or if you believe that this message was given in error.""" ,
88- cause );
98+ or if you believe that this message was given in error.""" , cause );
8999 } catch (Exception cause ) {
90100 throw new IllegalStateException ("""
91101 %s requested but %s %s could not be loaded.
92102 Maybe add the %s dependency, or %s.
93103 Note: %s %s is a commercial product.
94- Visit https://timefold.ai to find out more, or contact Timefold customer support."""
95- .formatted (feature .getName (), SOLVER_NAME , ENTERPRISE_NAME , feature .getWorkaround (),
96- ENTERPRISE_COORDINATES , SOLVER_NAME , ENTERPRISE_NAME ),
97- cause );
104+ Visit https://timefold.ai to find out more, or contact Timefold customer support.""" .formatted (
105+ feature .getName (), SOLVER_NAME , ENTERPRISE_NAME , feature .getWorkaround (), ENTERPRISE_COORDINATES ,
106+ SOLVER_NAME , ENTERPRISE_NAME ), cause );
98107 }
99108 }
100109
@@ -146,21 +155,18 @@ <Solution_> DestinationSelector<Solution_> applyNearbySelection(DestinationSelec
146155 SelectionOrder resolvedSelectionOrder , ElementDestinationSelector <Solution_ > destinationSelector );
147156
148157 <Solution_ > AbstractMoveSelectorFactory <Solution_ , MultistageMoveSelectorConfig >
149- buildBasicMultistageMoveSelectorFactory (
150- MultistageMoveSelectorConfig moveSelectorConfig );
158+ buildBasicMultistageMoveSelectorFactory (MultistageMoveSelectorConfig moveSelectorConfig );
151159
152160 <Solution_ > AbstractMoveSelectorFactory <Solution_ , ListMultistageMoveSelectorConfig >
153- buildListMultistageMoveSelectorFactory (
154- ListMultistageMoveSelectorConfig moveSelectorConfig );
161+ buildListMultistageMoveSelectorFactory (ListMultistageMoveSelectorConfig moveSelectorConfig );
155162
156163 InnerConstraintProfiler buildConstraintProfiler ();
157164
158165 enum Feature {
159166 MULTITHREADED_SOLVING ("Multi-threaded solving" , "remove moveThreadCount from solver configuration" ),
160167 PARTITIONED_SEARCH ("Partitioned search" , "remove partitioned search phase from solver configuration" ),
161168 NEARBY_SELECTION ("Nearby selection" , "remove nearby selection from solver configuration" ),
162- AUTOMATIC_NODE_SHARING ("Automatic node sharing" ,
163- "remove automatic node sharing from solver configuration" ),
169+ AUTOMATIC_NODE_SHARING ("Automatic node sharing" , "remove automatic node sharing from solver configuration" ),
164170 MULTISTAGE_MOVE ("Multistage move selector" ,
165171 "remove multistageMoveSelector and/or listMultistageMoveSelector from the solver configuration" ),
166172 CONSTRAINT_PROFILING ("Constraint profiling" , "remove constraintStreamProfilingEnabled from the solver configuration" );
0 commit comments