com.hcl.domino.misc.JNXServiceFinder Maven / Gradle / Ivy
/*
* ==========================================================================
* Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
* All rights reserved.
* ==========================================================================
* Licensed under the Apache License, Version 2.0 (the "License"). You may
* not use this file except in compliance with the License. You may obtain a
* copy of the License at .
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
* ==========================================================================
*/
package com.hcl.domino.misc;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
/**
* Utility class to coordinate service loading for JNX.
*
* @author Jesse Gallagher
* @since 1.0.12
*/
public enum JNXServiceFinder {
;
private static final Map, Collection>> SERVICE_CACHE = new ConcurrentHashMap<>();
private static final Map, Object> REQUIRED_SERVICE_CACHE = new ConcurrentHashMap<>();
/**
* Finds a single implementation of the provided service class using the
* provided classloader, throwing an exception if none can be found.
*
* The {@link ClassLoader} parameter is intended for signaling the
* expected loader for a service and is not intended for multi-classloader
* environments. This method will store retrieved instances in an internal
* cache and will not look them up again, regardless of {@code cl}
* parameter.
*
* Service implementations looked up by this method are expected to
* be stateless.
*
* @param the type of service to load
* @param serviceClass a {@link Class} object representing {@code }
* @param cl the {@link ClassLoader} to use to load services
* @return an instance of the provided service class
* @throws NoSuchElementException if no implementation can be found
*/
@SuppressWarnings("unchecked")
public static T findRequiredService(final Class serviceClass, final ClassLoader cl) {
return (T)REQUIRED_SERVICE_CACHE.computeIfAbsent(serviceClass, c -> {
final Iterable> services = AccessController
.doPrivileged((PrivilegedAction>) () -> ServiceLoader.load(c, cl));
return services.iterator().next();
});
}
/**
* Finds services implementing the provided service class using the context
* classloader.
*
* Service implementations looked up by this method are expected to
* be stateless.
*
* @param the type of service to load
* @param serviceClass a {@link Class} object representing {@code }
* @return a {@link Stream} of service implementations
*/
@SuppressWarnings("unchecked")
public static Stream findServices(final Class serviceClass) {
return (Stream)SERVICE_CACHE.computeIfAbsent(serviceClass, c -> {
Set converters = new TreeSet<>(Comparator.comparing(o -> o.getClass().getName()));
// Check the context (app) ClassLoader
Iterable services = AccessController
.doPrivileged((PrivilegedAction>) () -> ServiceLoader.load(serviceClass));
services.forEach(converters::add);
// Also check the service's own ClassLoader, as it may be distinct
services = AccessController
.doPrivileged((PrivilegedAction>) () -> ServiceLoader.load(serviceClass, serviceClass.getClassLoader()));
services.forEach(converters::add);
return converters;
}).stream();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy