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 volatile 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,21 @@ static String identifySolverVersion() {
6269 }
6370
6471 @ SuppressWarnings ("unchecked" )
65- static TimefoldSolverEnterpriseService load ()
66- throws ClassNotFoundException , NoSuchMethodException , InvocationTargetException , IllegalAccessException {
67- // Avoids ServiceLoader by using reflection directly.
68- var clz = (Class <? extends TimefoldSolverEnterpriseService >) Class
69- .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 );
72+ static TimefoldSolverEnterpriseService load () throws ClassNotFoundException , NoSuchMethodException ,
73+ InvocationTargetException , IllegalAccessException , InstantiationException {
74+ if (InstanceCarrier .INSTANCE == null ) {
75+ synchronized (InstanceCarrier .class ) {
76+ if (InstanceCarrier .INSTANCE == null ) {
77+ // Avoids ServiceLoader by using reflection directly.
78+ var clz = (Class <? extends TimefoldSolverEnterpriseService >) Class
79+ .forName ("ai.timefold.solver.enterprise.core.DefaultTimefoldSolverEnterpriseService" );
80+ var ctor = clz .getDeclaredConstructor (Function .class );
81+ InstanceCarrier .INSTANCE =
82+ ctor .newInstance ((Function <Class <?>, String >) TimefoldSolverEnterpriseService ::getVersionString );
83+ }
84+ }
85+ }
86+ return InstanceCarrier .INSTANCE ;
7387 }
7488
7589 static String getVersionString (Class <?> clz ) {
@@ -84,17 +98,15 @@ static TimefoldSolverEnterpriseService loadOrFail(Feature feature) {
8498 throw new IllegalStateException ("""
8599 No valid Timefold Enterprise License was found.
86100 Please contact Timefold to obtain a valid license,
87- or if you believe that this message was given in error.""" ,
88- cause );
101+ or if you believe that this message was given in error.""" , cause );
89102 } catch (Exception cause ) {
90103 throw new IllegalStateException ("""
91104 %s requested but %s %s could not be loaded.
92105 Maybe add the %s dependency, or %s.
93106 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 );
107+ Visit https://timefold.ai to find out more, or contact Timefold customer support.""" .formatted (
108+ feature .getName (), SOLVER_NAME , ENTERPRISE_NAME , feature .getWorkaround (), ENTERPRISE_COORDINATES ,
109+ SOLVER_NAME , ENTERPRISE_NAME ), cause );
98110 }
99111 }
100112
@@ -146,21 +158,18 @@ <Solution_> DestinationSelector<Solution_> applyNearbySelection(DestinationSelec
146158 SelectionOrder resolvedSelectionOrder , ElementDestinationSelector <Solution_ > destinationSelector );
147159
148160 <Solution_ > AbstractMoveSelectorFactory <Solution_ , MultistageMoveSelectorConfig >
149- buildBasicMultistageMoveSelectorFactory (
150- MultistageMoveSelectorConfig moveSelectorConfig );
161+ buildBasicMultistageMoveSelectorFactory (MultistageMoveSelectorConfig moveSelectorConfig );
151162
152163 <Solution_ > AbstractMoveSelectorFactory <Solution_ , ListMultistageMoveSelectorConfig >
153- buildListMultistageMoveSelectorFactory (
154- ListMultistageMoveSelectorConfig moveSelectorConfig );
164+ buildListMultistageMoveSelectorFactory (ListMultistageMoveSelectorConfig moveSelectorConfig );
155165
156166 InnerConstraintProfiler buildConstraintProfiler ();
157167
158168 enum Feature {
159169 MULTITHREADED_SOLVING ("Multi-threaded solving" , "remove moveThreadCount from solver configuration" ),
160170 PARTITIONED_SEARCH ("Partitioned search" , "remove partitioned search phase from solver configuration" ),
161171 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" ),
172+ AUTOMATIC_NODE_SHARING ("Automatic node sharing" , "remove automatic node sharing from solver configuration" ),
164173 MULTISTAGE_MOVE ("Multistage move selector" ,
165174 "remove multistageMoveSelector and/or listMultistageMoveSelector from the solver configuration" ),
166175 CONSTRAINT_PROFILING ("Constraint profiling" , "remove constraintStreamProfilingEnabled from the solver configuration" );
0 commit comments