Skip to content

Commit 571e3ca

Browse files
committed
Add 'InstantiatorService' to allow customization of object construction
1 parent d0256d1 commit 571e3ca

File tree

19 files changed

+659
-522
lines changed

19 files changed

+659
-522
lines changed

ehcache-core/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
plugins {
1818
id 'org.ehcache.build.internal-module'
19+
id 'java-test-fixtures'
1920
}
2021

2122
publishing.publications.withType(MavenPublication) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright Terracotta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.ehcache.core.spi.service;
17+
18+
import org.ehcache.spi.service.Service;
19+
20+
public interface InstantiatorService extends Service {
21+
22+
<T> T instantiate(Class<T> clazz, Object[] arguments);
23+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Terracotta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.ehcache.core.spi;
17+
18+
import org.ehcache.spi.service.Service;
19+
20+
import java.util.function.UnaryOperator;
21+
22+
public interface ServiceLocatorUtils {
23+
24+
static <T extends Service> void withServiceLocator(T service, ServiceTask<T> task) throws Exception {
25+
withServiceLocator(service, UnaryOperator.identity(), task);
26+
}
27+
28+
static <T extends Service> void withServiceLocator(T service, UnaryOperator<ServiceLocator.DependencySet> dependencies, ServiceTask<T> task) throws Exception {
29+
ServiceLocator serviceLocator = dependencies.apply(ServiceLocator.dependencySet().with(service)).build();
30+
serviceLocator.startAllServices();
31+
try {
32+
task.execute(service);
33+
} finally {
34+
serviceLocator.stopAllServices();
35+
}
36+
}
37+
38+
@FunctionalInterface
39+
interface ServiceTask<T extends Service> {
40+
41+
void execute(T service) throws Exception;
42+
}
43+
44+
}

ehcache-impl/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dependencies {
5555
implementation group: 'org.terracotta', name: 'terracotta-utilities-tools', version: parent.terracottaUtilitiesVersion
5656
compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0'
5757
testImplementation project(':core-spi-test')
58+
testImplementation testFixtures(project(':ehcache-core'))
5859
testImplementation 'org.ow2.asm:asm:6.2'
5960
testImplementation 'org.ow2.asm:asm-commons:6.2'
6061
testImplementation ("org.terracotta:statistics:$parent.statisticVersion")

ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ public class UserManagedCacheBuilder<K, V, T extends UserManagedCache<K, V>> imp
107107
private final Class<K> keyType;
108108
private final Class<V> valueType;
109109
private String id;
110-
private final Set<Service> services = new HashSet<>();
111-
private final Set<ServiceCreationConfiguration<?, ?>> serviceCreationConfigurations = new HashSet<>();
110+
private ServiceLocator.DependencySet dependencySet = ServiceLocator.dependencySet().with(Store.Provider.class);
112111
private ExpiryPolicy<? super K, ? super V> expiry = ExpiryPolicy.NO_EXPIRY;
113112
private ClassLoader classLoader = ClassLoading.getDefaultClassLoader();
114113
private EvictionAdvisor<? super K, ? super V> evictionAdvisor;
@@ -143,8 +142,7 @@ private UserManagedCacheBuilder(UserManagedCacheBuilder<K, V, T> toCopy) {
143142
this.keyType = toCopy.keyType;
144143
this.valueType = toCopy.valueType;
145144
this.id = toCopy.id;
146-
this.services.addAll(toCopy.services);
147-
this.serviceCreationConfigurations.addAll(toCopy.serviceCreationConfigurations);
145+
this.dependencySet = toCopy.dependencySet;
148146
this.expiry = toCopy.expiry;
149147
this.classLoader = toCopy.classLoader;
150148
this.evictionAdvisor = toCopy.evictionAdvisor;
@@ -167,19 +165,15 @@ private UserManagedCacheBuilder(UserManagedCacheBuilder<K, V, T> toCopy) {
167165
}
168166

169167
@SuppressWarnings("try")
170-
T build(ServiceLocator.DependencySet serviceLocatorBuilder) throws IllegalStateException {
168+
T internalBuild() throws IllegalStateException {
171169

172170
String alias = id == null ? "UserManaged - " + instanceId.getAndIncrement() : id;
173171
try(EhcachePrefixLoggerFactory.Context ignored = EhcachePrefixLoggerFactory.withContext("cache-alias", alias)){
174172
validateListenerConfig();
175173

176174
ServiceLocator serviceLocator;
177175
try {
178-
for (ServiceCreationConfiguration<?, ?> serviceCreationConfig : serviceCreationConfigurations) {
179-
serviceLocatorBuilder = serviceLocatorBuilder.with(serviceCreationConfig);
180-
}
181-
serviceLocatorBuilder = serviceLocatorBuilder.with(Store.Provider.class);
182-
serviceLocator = serviceLocatorBuilder.build();
176+
serviceLocator = dependencySet.build();
183177
serviceLocator.startAllServices();
184178
} catch (Exception e) {
185179
throw new IllegalStateException("UserManagedCacheBuilder failed to build.", e);
@@ -354,13 +348,7 @@ private void validateListenerConfig() {
354348

355349
private void registerListeners(Cache<K, V> cache, ServiceProvider<Service> serviceProvider, List<LifeCycled> lifeCycledList) {
356350
if (!eventListenerConfigurations.isEmpty()) {
357-
final CacheEventListenerProvider listenerProvider;
358-
CacheEventListenerProvider provider;
359-
if ((provider = serviceProvider.getService(CacheEventListenerProvider.class)) != null) {
360-
listenerProvider = provider;
361-
} else {
362-
listenerProvider = new DefaultCacheEventListenerProvider();
363-
}
351+
final CacheEventListenerProvider listenerProvider = serviceProvider.getService(CacheEventListenerProvider.class);
364352
for (CacheEventListenerConfiguration<?> config : eventListenerConfigurations) {
365353
final CacheEventListener<K, V> listener = listenerProvider.createEventListener(id, config);
366354
if (listener != null) {
@@ -396,7 +384,7 @@ T cast(UserManagedCache<K, V> cache) {
396384
* @throws IllegalStateException if the user managed cache cannot be built
397385
*/
398386
public final T build(final boolean init) throws IllegalStateException {
399-
final T build = build(dependencySet().withoutMandatoryServices().with(services));
387+
final T build = internalBuild();
400388
if (init) {
401389
build.init();
402390
}
@@ -557,6 +545,7 @@ public final UserManagedCacheBuilder<K, V, T> withEventListeners(CacheEventListe
557545
*/
558546
public final UserManagedCacheBuilder<K, V, T> withEventListeners(CacheEventListenerConfiguration<?> ... cacheEventListenerConfigurations) {
559547
UserManagedCacheBuilder<K, V, T> otherBuilder = new UserManagedCacheBuilder<>(this);
548+
otherBuilder.dependencySet.with(CacheEventListenerProvider.class);
560549
otherBuilder.eventListenerConfigurations.addAll(Arrays.asList(cacheEventListenerConfigurations));
561550
return otherBuilder;
562551
}
@@ -737,9 +726,8 @@ public UserManagedCacheBuilder<K, V, T> withValueSerializer(Serializer<V> valueS
737726
@Deprecated
738727
public UserManagedCacheBuilder<K, V, T> withSizeOfMaxObjectGraph(long size) {
739728
UserManagedCacheBuilder<K, V, T> otherBuilder = new UserManagedCacheBuilder<>(this);
740-
removeAnySizeOfEngine(otherBuilder);
741729
otherBuilder.objectGraphSize = size;
742-
otherBuilder.serviceCreationConfigurations.add(new org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration(otherBuilder.maxObjectSize, otherBuilder.sizeOfUnit, otherBuilder.objectGraphSize));
730+
otherBuilder.dependencySet.with(new org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration(otherBuilder.maxObjectSize, otherBuilder.sizeOfUnit, otherBuilder.objectGraphSize));
743731
return otherBuilder;
744732
}
745733

@@ -756,10 +744,9 @@ public UserManagedCacheBuilder<K, V, T> withSizeOfMaxObjectGraph(long size) {
756744
@Deprecated
757745
public UserManagedCacheBuilder<K, V, T> withSizeOfMaxObjectSize(long size, MemoryUnit unit) {
758746
UserManagedCacheBuilder<K, V, T> otherBuilder = new UserManagedCacheBuilder<>(this);
759-
removeAnySizeOfEngine(otherBuilder);
760747
otherBuilder.maxObjectSize = size;
761748
otherBuilder.sizeOfUnit = unit;
762-
otherBuilder.serviceCreationConfigurations.add(new org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration(otherBuilder.maxObjectSize, otherBuilder.sizeOfUnit, otherBuilder.objectGraphSize));
749+
otherBuilder.dependencySet.with(new org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration(otherBuilder.maxObjectSize, otherBuilder.sizeOfUnit, otherBuilder.objectGraphSize));
763750
return otherBuilder;
764751
}
765752

@@ -791,10 +778,7 @@ public static <K, V> UserManagedCacheBuilder<K, V, UserManagedCache<K, V>> newUs
791778
@SuppressWarnings("deprecation")
792779
public UserManagedCacheBuilder<K, V, T> using(Service service) {
793780
UserManagedCacheBuilder<K, V, T> otherBuilder = new UserManagedCacheBuilder<>(this);
794-
if (service instanceof org.ehcache.core.spi.store.heap.SizeOfEngineProvider) {
795-
removeAnySizeOfEngine(otherBuilder);
796-
}
797-
otherBuilder.services.add(service);
781+
otherBuilder.dependencySet.with(service);
798782
return otherBuilder;
799783
}
800784

@@ -815,17 +799,7 @@ public UserManagedCacheBuilder<K, V, T> using(Service service) {
815799
@SuppressWarnings("deprecation")
816800
public UserManagedCacheBuilder<K, V, T> using(ServiceCreationConfiguration<?, ?> serviceConfiguration) {
817801
UserManagedCacheBuilder<K, V, T> otherBuilder = new UserManagedCacheBuilder<>(this);
818-
if (serviceConfiguration instanceof org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration) {
819-
removeAnySizeOfEngine(otherBuilder);
820-
}
821-
otherBuilder.serviceCreationConfigurations.add(serviceConfiguration);
802+
otherBuilder.dependencySet.with(serviceConfiguration);
822803
return otherBuilder;
823804
}
824-
825-
@Deprecated
826-
private static void removeAnySizeOfEngine(UserManagedCacheBuilder<?, ?, ?> builder) {
827-
builder.services.remove(findSingletonAmongst(org.ehcache.core.spi.store.heap.SizeOfEngineProvider.class, builder.services));
828-
builder.serviceCreationConfigurations.remove(findSingletonAmongst(org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration.class, builder.serviceCreationConfigurations));
829-
}
830-
831805
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright Terracotta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.ehcache.impl.internal;
17+
18+
import org.ehcache.core.spi.service.InstantiatorService;
19+
import org.ehcache.core.spi.service.ServiceFactory;
20+
import org.ehcache.impl.internal.classes.DefaultInstantiatorService;
21+
import org.ehcache.spi.service.ServiceCreationConfiguration;
22+
import org.osgi.service.component.annotations.Component;
23+
24+
@Component
25+
public class DefaultInstantiatorServiceFactory implements ServiceFactory<InstantiatorService> {
26+
27+
@Override
28+
public int rank() {
29+
return Integer.MIN_VALUE;
30+
}
31+
32+
@Override
33+
public InstantiatorService create(ServiceCreationConfiguration<InstantiatorService, ?> configuration) {
34+
return new DefaultInstantiatorService();
35+
}
36+
37+
@Override
38+
public Class<? extends InstantiatorService> getServiceType() {
39+
return DefaultInstantiatorService.class;
40+
}
41+
}

ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@
1717
package org.ehcache.impl.internal.classes;
1818

1919
import org.ehcache.config.CacheConfiguration;
20+
import org.ehcache.core.spi.service.InstantiatorService;
21+
import org.ehcache.spi.service.ServiceDependencies;
2022
import org.ehcache.spi.service.ServiceProvider;
2123
import org.ehcache.spi.service.Service;
2224
import org.ehcache.spi.service.ServiceConfiguration;
2325
import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap;
2426

2527
import java.io.Closeable;
2628
import java.io.IOException;
27-
import java.lang.reflect.InvocationTargetException;
2829
import java.util.Collections;
2930
import java.util.Iterator;
3031
import java.util.LinkedHashMap;
3132
import java.util.Map;
3233
import java.util.Set;
3334
import java.util.concurrent.atomic.AtomicInteger;
3435

35-
import static org.ehcache.impl.internal.classes.commonslang.reflect.ConstructorUtils.invokeConstructor;
3636
import static org.ehcache.core.spi.service.ServiceUtils.findAmongst;
3737
import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst;
3838

3939
/**
4040
* @author Alex Snaps
4141
*/
42-
public class ClassInstanceProvider<K, C extends ClassInstanceConfiguration<? extends T>, T> {
42+
@ServiceDependencies(InstantiatorService.class)
43+
public class ClassInstanceProvider<K, C extends ClassInstanceConfiguration<? extends T>, T> implements Service {
4344

4445
/**
4546
* The order in which entries are put in is kept.
@@ -55,6 +56,8 @@ public class ClassInstanceProvider<K, C extends ClassInstanceConfiguration<? ext
5556
private final Class<C> cacheLevelConfig;
5657
private final boolean uniqueClassLevelConfig;
5758

59+
private InstantiatorService instantiator;
60+
5861
protected ClassInstanceProvider(ClassInstanceProviderConfiguration<K, C> factoryConfig,
5962
Class<C> cacheLevelConfig) {
6063
this(factoryConfig, cacheLevelConfig, false);
@@ -117,12 +120,8 @@ private T newInstance(K alias, ClassInstanceConfiguration<? extends T> config) {
117120
if(config.getInstance() != null) {
118121
instance = config.getInstance();
119122
} else {
120-
try {
121-
instance = invokeConstructor(config.getClazz(), config.getArguments());
122-
instantiated.add(instance);
123-
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
124-
throw new RuntimeException(e);
125-
}
123+
instance = instantiator.instantiate(config.getClazz(), config.getArguments());
124+
instantiated.add(instance);
126125
}
127126

128127
AtomicInteger currentCount = providedVsCount.putIfAbsent(instance, new AtomicInteger(1));
@@ -153,10 +152,10 @@ protected void releaseInstance(T instance) throws IOException {
153152
}
154153

155154
public void start(ServiceProvider<Service> serviceProvider) {
156-
// default no-op
155+
this.instantiator = serviceProvider.getService(InstantiatorService.class);
157156
}
158157

159158
public void stop() {
160-
// default no-op
159+
this.instantiator = null;
161160
}
162161
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Terracotta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.ehcache.impl.internal.classes;
17+
18+
import org.ehcache.core.spi.service.InstantiatorService;
19+
import org.ehcache.impl.internal.classes.commonslang.reflect.ConstructorUtils;
20+
import org.ehcache.spi.service.Service;
21+
import org.ehcache.spi.service.ServiceProvider;
22+
23+
public class DefaultInstantiatorService implements InstantiatorService {
24+
25+
@Override
26+
public void start(ServiceProvider<Service> serviceProvider) {
27+
28+
}
29+
30+
@Override
31+
public void stop() {
32+
33+
}
34+
35+
@Override
36+
public <T> T instantiate(Class<T> clazz, Object[] arguments) {
37+
try {
38+
return ConstructorUtils.invokeConstructor(clazz, arguments);
39+
} catch (ReflectiveOperationException e) {
40+
throw new RuntimeException(e);
41+
}
42+
}
43+
}

ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.ehcache.impl.internal.spi.resilience;
1717

1818
import org.ehcache.config.CacheConfiguration;
19+
import org.ehcache.core.spi.service.InstantiatorService;
1920
import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration;
2021
import org.ehcache.impl.config.resilience.DefaultResilienceStrategyProviderConfiguration;
2122
import org.ehcache.impl.internal.classes.ClassInstanceProvider;
@@ -24,10 +25,12 @@
2425
import org.ehcache.spi.resilience.ResilienceStrategy;
2526
import org.ehcache.spi.resilience.ResilienceStrategyProvider;
2627
import org.ehcache.spi.service.Service;
28+
import org.ehcache.spi.service.ServiceDependencies;
2729
import org.ehcache.spi.service.ServiceProvider;
2830

2931
import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst;
3032

33+
@ServiceDependencies(InstantiatorService.class)
3134
public class DefaultResilienceStrategyProvider implements ResilienceStrategyProvider {
3235

3336
private final ComponentProvider regularStrategies;

ehcache-impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ org.ehcache.impl.internal.store.tiering.TieredStoreProviderFactory
55
org.ehcache.impl.internal.store.tiering.CompoundCachingTierProviderFactory
66
org.ehcache.impl.internal.store.loaderwriter.LoaderWriterStoreProviderFactory
77

8+
org.ehcache.impl.internal.DefaultInstantiatorServiceFactory
89
org.ehcache.impl.internal.TimeSourceServiceFactory
910
org.ehcache.impl.internal.spi.serialization.DefaultSerializationProviderFactory
1011
org.ehcache.impl.internal.spi.loaderwriter.DefaultCacheLoaderWriterProviderFactory

0 commit comments

Comments
 (0)