0

When I try to obtain an instance of a Class through ServiceLoader on a TaskManager node the class cannot be loaded, basically I cannot access the classes declared in META-INF/services via ServiceLoader at Runtime.

I am using ShardignSphere to broadcast some data from the database, but the framework relies on some dependencies being loaded by the Java SPI, is there any way to solve the problem? In previous versions (4.x.x) I used to do it with Reflection but with the new version of ShardingSphere (5.3.1) there would be too many things that need to be loaded and it wouldn't be feasible, what's the correct way to do this?

EDIT:

ShardingSphere stackTrace: this is the first occurrence of a precontition check where a Class that is supposed to be loaded via SPI isn't loaded.

 Collection<T> foundRules = findRules(clazz);
        Preconditions.checkState(1 == foundRules.size(), "Rule `%s` should have and only have one instance.", clazz.getSimpleName());
        return foundRules.iterator().next(); 
java.lang.IllegalStateException: Rule `TrafficRule` should have and only have one instance.
    at com.google.common.base.Preconditions.checkState(Preconditions.java:591)
    at org.apache.shardingsphere.infra.metadata.database.rule.ShardingSphereRuleMetaData.getSingleRule(ShardingSphereRuleMetaData.java:91)
    at org.apache.shardingsphere.driver.jdbc.core.connection.ConnectionManager.getTrafficDataSourceMap(ConnectionManager.java:88)
    at org.apache.shardingsphere.driver.jdbc.core.connection.ConnectionManager.<init>(ConnectionManager.java:82)
    at org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection.<init>(ShardingSphereConnection.java:71)
    at org.apache.shardingsphere.driver.state.ok.OKDriverState.getConnection(OKDriverState.java:34)
    at org.apache.shardingsphere.driver.state.DriverStateContext.getConnection(DriverStateContext.java:44)
    at org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource.getConnection(ShardingSphereDataSource.java:86)
    at com.huawei.networkops.flink.monitor.pingmesh.broadcast.PingmeshDataResolver.update(PingmeshDataResolver.java:77)
    at com.huawei.networkops.flink.monitor.pingmesh.processor.PingmeshDBSourceProcessor$PingmeshBroadcastDataBuilder.buildBroadcastData(PingmeshDBSourceProcessor.java:39)
    at com.huawei.networkops.flink.monitor.pingmesh.processor.PingmeshDBSourceProcessor$PingmeshBroadcastDataBuilder.buildBroadcastData(PingmeshDBSourceProcessor.java:30)
    at com.huawei.networkops.flink.monitor.common.broadcast.AbstractDataBroadcastStreamProcessor$DataBroadcastFunction.run(AbstractDataBroadcastStreamProcessor.java:166)
    at org.apache.flink.streaming.api.operators.StreamSource.run(StreamSource.java:94)
    at org.apache.flink.streaming.api.operators.StreamSource.run(StreamSource.java:58)
    at org.apache.flink.streaming.runtime.tasks.SourceStreamTask.run(SourceStreamTask.java:99)
    at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:300)
    at org.apache.flink.runtime.taskmanager.Task.run(Task.java:704)
    at java.lang.Thread.run(Thread.java:748)

This is how ShardignSphere SPI loads its dependencies:

    private Collection<T> load() {
        Collection<T> result = new LinkedList<>();
        for (T each : ServiceLoader.load(serviceInterface)) {
            result.add(each);
        }
        return result;
    }
public final class ShardingSphereServiceLoader<T> {
    
    private static final Map<Class<?>, ShardingSphereServiceLoader<?>> LOADERS = new ConcurrentHashMap<>();
    
    private final Class<T> serviceInterface;
    
    @Getter
    private final Collection<T> services;
    
    private ShardingSphereServiceLoader(final Class<T> serviceInterface) {
        this.serviceInterface = serviceInterface;
        validate();
        services = load();
    }
    
    private void validate() {
        Preconditions.checkNotNull(serviceInterface, "SPI interface is null.");
        Preconditions.checkArgument(serviceInterface.isInterface(), "SPI interface `%s` is not interface.", serviceInterface);
    }
    
    private Collection<T> load() {
        Collection<T> result = new LinkedList<>();
        for (T each : ServiceLoader.load(serviceInterface)) {
            result.add(each);
        }
        return result;
    }
    
    /**
     * Get service instances.
     *
     * @param serviceInterface service interface
     * @param <T> type of service interface
     * @return service instances
     * @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">JDK-8161372</a>
     */
    @SuppressWarnings("unchecked")
    public static <T> Collection<T> getServiceInstances(final Class<T> serviceInterface) {
        ShardingSphereServiceLoader<?> result = LOADERS.get(serviceInterface);
        return (Collection<T>) (null != result ? result.getServiceInstances() : LOADERS.computeIfAbsent(serviceInterface, ShardingSphereServiceLoader::new).getServiceInstances());
    }
    
    private Collection<T> getServiceInstances() {
        return null == serviceInterface.getAnnotation(SingletonSPI.class) ? createNewServiceInstances() : getSingletonServiceInstances();
    }
    
    @SneakyThrows(ReflectiveOperationException.class)
    @SuppressWarnings("unchecked")
    private Collection<T> createNewServiceInstances() {
        Collection<T> result = new LinkedList<>();
        for (Object each : services) {
            result.add((T) each.getClass().getDeclaredConstructor().newInstance());
        }
        return result;
    }
    
    private Collection<T> getSingletonServiceInstances() {
        return services;
    }
}

Teddy Tsai
  • 414
  • 1
  • 13
  • Show us the code and the error you're getting – Jorn Jun 09 '23 at 09:06
  • @Jorn edited question, added some context – Teddy Tsai Jun 09 '23 at 09:13
  • Doesn't really look like a class access issue to me. The exception message gives you something concrete to look at. I'm not familiar with ShardingSphere, but it seems to be an issue with the TrafficRule you haven't shown. – Jorn Jun 09 '23 at 09:20
  • @Jorn TrafficRules is one of the many Classes ShardingSphere uses the SPI to load, I have looked at how the rules are loaded in the ShardingSphere source code and the rules and the problem starts with load() in the ShardingSphereServiceLoader class. – Teddy Tsai Jun 09 '23 at 09:28

0 Answers0