com.jwebmp.guicedinjection.GuiceContext Maven / Gradle / Ivy
Show all versions of guiced-injection Show documentation
/*
* Copyright (C) 2017 GedMarc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jwebmp.guicedinjection;
import com.google.common.base.Stopwatch;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.jwebmp.guicedinjection.interfaces.*;
import com.jwebmp.guicedinjection.interfaces.annotations.INotEnhanceable;
import com.jwebmp.guicedinjection.interfaces.annotations.INotInjectable;
import com.jwebmp.logger.LogFactory;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ResourceList;
import io.github.classgraph.ScanResult;
import javax.validation.constraints.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.jwebmp.guicedinjection.interfaces.IDefaultService.*;
/**
* Provides an interface for reflection and injection in one.
*
* Use reflect() to access the class library, or inject() to get the injector for any instance
*
* @author GedMarc
* @version 1.0
* @since Nov 14, 2016
*/
@SuppressWarnings("MissingClassJavaDoc")
public class GuiceContext
{
/**
* Field log
*/
private static final Logger log = LogFactory.getLog("GuiceContext");
/**
* This particular instance of the class
*/
private static final GuiceContext instance = new GuiceContext();
/**
* A list of all the loaded singleton sets
*/
@SuppressWarnings("unchecked")
private static final Map allLoadedServices = Collections.synchronizedMap(new LinkedHashMap());
/**
* The building injector
*/
private static boolean buildingInjector = false;
/**
* The configuration object
*/
private static GuiceConfig> config;
/**
* The physical injector for the JVM container
*/
private Injector injector;
/**
* The actual scanner
*/
private ClassGraph scanner;
/**
* The scan result built from everything - the core scanner.
*/
private ScanResult scanResult;
public static long defaultWaitTime = 1;
public static TimeUnit defaultWaitUnit = TimeUnit.MILLISECONDS;
/**
* Creates a new Guice context. Not necessary
*/
private GuiceContext()
{
//No config required
}
/**
* Reference the Injector Directly
*
* @return The global Guice Injector Object, Never Null, Instantiates the Injector if not configured
*/
@NotNull
@SuppressWarnings("unchecked")
public static synchronized Injector inject()
{
if (GuiceContext.buildingInjector)
{
throw new RuntimeException(
"The injector is being called recursively during build. Place such actions in a IGuicePostStartup or use the IGuicePreStartup Service Loader.");
}
if (GuiceContext.instance().injector == null)
{
try
{
GuiceContext.buildingInjector = true;
GuiceContext.log.info("Starting up Guice Context");
GuiceContext.instance()
.loadPreStartups();
GuiceContext.instance()
.loadConfiguration();
if (GuiceContext.instance()
.getConfig()
.isPathScanning() ||
GuiceContext.instance()
.getConfig()
.isClasspathScanning())
{
GuiceContext.instance()
.loadScanner();
}
List cModules = GuiceContext.instance()
.loadDefaultBinders();
GuiceContext.instance().injector = Guice.createInjector(cModules);
GuiceContext.buildingInjector = false;
GuiceContext.instance()
.loadPostStartups();
Runtime.getRuntime()
.addShutdownHook(new Thread(GuiceContext::destroy));
GuiceContext.log.config("Injection System Ready");
}
catch (Throwable e)
{
GuiceContext.log.log(Level.SEVERE, "Exception creating Injector : " + e.getMessage(), e);
throw new RuntimeException("Unable to boot Guice Injector", e);
}
}
GuiceContext.buildingInjector = false;
return GuiceContext.instance().injector;
}
/**
* Gets a new injected instance of a class
*
* @param
* The type to retrieve
* @param type
* The physical class object
*
* @return The scoped object
*/
@NotNull
public static T get(@NotNull Class type)
{
return get(type, null);
}
/**
* Gets a new injected instance of a class
*
* @deprecated For get()
*
* @param
* The type to retrieve
* @param type
* The physical class object
*
* @return The scoped object
*/
@NotNull
@Deprecated()
public static T getInstance(@NotNull Class type)
{
return get(type, null);
}
/**
* Gets a new injected instance of a class
*
* @deprecated For get()
*
* @param
* The type to retrieve
* @param type
* The physical class object
*
* @return The scoped object
*/
@NotNull
@Deprecated()
public static T getInstance(@NotNull Key type)
{
return get(type);
}
/**
* Gets a new injected instance of a class
*
* @deprecated For get()
*
* @param
* The type to retrieve
* @param type
* The physical class object
*
* @return The scoped object
*/
@NotNull
@Deprecated()
public static T getInstance(@NotNull Class type,Class extends Annotation> annotation)
{
return get(type, annotation);
}
private static boolean isEntityType(Class> clazz)
{
for (Annotation annotation : clazz.getAnnotations())
{
if (annotation.annotationType()
.getCanonicalName()
.equalsIgnoreCase("javax.persistence.Entity"))
{
return true;
}
}
return false;
}
private static boolean isNotEnhanceable(Class> clazz)
{
return clazz.isAnnotationPresent(INotEnhanceable.class);
}
private static boolean isNotInjectable(Class> clazz)
{
return clazz.isAnnotationPresent(INotInjectable.class);
}
/**
* Gets a new injected instance of a class
*
* @param
* The type to retrieve
* @param type
* The physical class object
* @param annotation
* The annotation to fetch
*
* @return The scoped object
*/
public static T get(@NotNull Class type, Class extends Annotation> annotation)
{
if (annotation == null)
{
return get(Key.get(type));
}
return get(Key.get(type, annotation));
}
/**
* Gets a new specified instance from a give key
*
* @param
* The type to retrieve
* @param type
* The physical class object
*
* @return The scoped object
*/
@NotNull
public static T get(@NotNull Key type)
{
Class clazz = (Class) type.getTypeLiteral()
.getRawType();
T instance = null;
boolean isEntityType = isEntityType(clazz);
if (isNotEnhanceable(clazz) || isEntityType)
{
try
{
instance = clazz.getDeclaredConstructor()
.newInstance();
if (!isNotInjectable(clazz))
{
inject().injectMembers(instance);
}
}
catch (Exception e)
{
log.log(Level.SEVERE, "Unable to construct [" + clazz.getCanonicalName() + "]. Not Enhanceable or an Entity.", e);
throw new RuntimeException(e);
}
}
else
{
instance = inject().getInstance(type);
}
return instance;
}
/**
* Returns the actual context instance, provides access to methods existing a bit deeper
*
* @return The singleton instance of this
*/
public static GuiceContext instance()
{
return GuiceContext.instance;
}
/**
* Execute on Destroy
*/
@SuppressWarnings("unused")
public static void destroy()
{
Set destroyers = GuiceContext.instance()
.getLoader(IGuicePreDestroy.class, ServiceLoader.load(IGuicePreDestroy.class));
for (IGuicePreDestroy destroyer : destroyers)
{
IGuicePreDestroy instance = GuiceContext.get(destroyer.getClass());
instance.onDestroy();
}
if (GuiceContext.instance().scanResult != null)
{
GuiceContext.instance().scanResult.close();
}
if (GuiceContext.instance().scanResult != null)
{
GuiceContext.instance().scanResult.close();
}
GuiceContext.instance().scanResult = null;
GuiceContext.instance().scanner = null;
GuiceContext.instance().injector = null;
}
/**
* Returns the Java version as an int value.
*
* @return the Java version as an int value (8, 9, etc.)
*
* @since 12130
*/
public static int getJavaVersion()
{
String version = System.getProperty("java.version");
if (version.startsWith("1."))
{
version = version.substring(2);
}
// Allow these formats:
// 1.8.0_72-ea
// 9-ea
// 9
// 9.0.1
int dotPos = version.indexOf('.');
int dashPos = version.indexOf('-');
String value = version.substring(0, dotPos > -1 ? dotPos : dashPos > -1 ? dashPos : version.length());
return Integer.parseInt(value);
}
/**
* Method loadPreStartups gets the pre startups and loads them up
*/
private void loadPreStartups()
{
Set preStartups = getLoader(IGuicePreStartup.class, true, ServiceLoader.load(IGuicePreStartup.class));
List startups = new ArrayList<>(preStartups);
startups.sort(Comparator.comparing(IGuicePreStartup::sortOrder));
for (IGuicePreStartup startup : startups)
{
GuiceContext.log.config("Loading IGuicePreStartup - " +
startup.getClass()
.getCanonicalName());
startup.onStartup();
}
}
/**
* Returns the current scan result
*
* @return The physical Scan Result from the complete class scanner
*/
@NotNull
public ScanResult getScanResult()
{
if (scanResult == null)
{
loadScanner();
}
return scanResult;
}
/**
* Sets the current scan result
*
* @param scanResult
* The physical Scan Result from the complete class scanner
*/
@SuppressWarnings("unused")
public void setScanResult(ScanResult scanResult)
{
GuiceContext.instance().scanResult = scanResult;
}
/**
* Loads the IGuiceConfigurator
*/
private void loadConfiguration()
{
Set guiceConfigurators = getLoader(IGuiceConfigurator.class, true, ServiceLoader.load(IGuiceConfigurator.class));
if (GuiceContext.config == null)
{
GuiceContext.config = new GuiceConfig<>();
}
for (IGuiceConfigurator guiceConfigurator : guiceConfigurators)
{
GuiceContext.log.config("Loading IGuiceConfigurator - " +
guiceConfigurator.getClass()
.getCanonicalName());
guiceConfigurator.configure(GuiceContext.config);
}
GuiceContext.log.config("IGuiceConfigurator : " + GuiceContext.config.toString());
}
/**
* Starts up Guice and the scanner
*/
private void loadScanner()
{
Stopwatch stopwatch = Stopwatch.createStarted();
GuiceContext.log.info("Loading Classpath Scanner");
if (GuiceContext.config == null)
{
loadConfiguration();
}
scanner = new ClassGraph();
configureScanner(scanner);
try
{
scanResult = scanner.scan();
stopwatch.stop();
Map fileScans = quickScanFiles();
fileScans.forEach((key, value) ->
scanResult.getResourcesWithLeafName(key)
.forEachByteArray(value));
}
catch (Exception mpe)
{
GuiceContext.log.log(Level.SEVERE, "Unable to run scanner", mpe);
}
GuiceContext.log.fine("Loaded Classpath Scanner - Took [" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "] millis.");
}
/**
* Configures the scanner from its setup
*
* @param graph
* The ClassGraph to apply the configuration to
*/
private void configureScanner(ClassGraph graph)
{
if (config.isWhitelistPaths())
{
String[] paths = getPathsList();
if (paths.length != 0)
{
graph.whitelistPathsNonRecursive(paths);
}
}
if (GuiceContext.config.isExcludePaths())
{
String[] blacklistList = getPathsBlacklistList();
if (blacklistList.length != 0)
{
graph.blacklistPaths(blacklistList);
}
}
if (config.isWhitelistJarsAndModules())
{
if (getJavaVersion() < 9)
{
String[] jarBlacklist = getJarsWhiteList();
if (jarBlacklist.length != 0)
{
graph.blacklistJars(jarBlacklist);
}
}
else
{
String[] modulesBlacklist = getModulesWhiteList();
if (modulesBlacklist.length != 0)
{
graph.whitelistModules(modulesBlacklist);
}
}
}
if (GuiceContext.config.isExcludeModulesAndJars())
{
if (getJavaVersion() < 9)
{
String[] jarBlacklist = getJarsBlacklistList();
if (jarBlacklist.length != 0)
{
graph.blacklistJars(jarBlacklist);
}
}
else
{
String[] modulesBlacklist = getModulesBlacklistList();
if (modulesBlacklist.length != 0)
{
graph.blacklistModules(modulesBlacklist);
}
else
{
graph.ignoreParentModuleLayers();
}
}
}
if (GuiceContext.config.isWhiteListPackages())
{
String[] packages = getPackagesList();
if (packages.length != 0)
{
graph.whitelistPackages(packages);
}
}
if (GuiceContext.config.isBlackListPackages())
{
String[] packages = getBlacklistPackages();
if (packages.length != 0)
{
graph.blacklistPackages(packages);
}
}
if (GuiceContext.config.isExcludeParentModules())
{
graph.ignoreParentModuleLayers();
}
if (GuiceContext.config.isFieldInfo())
{
graph.enableFieldInfo();
}
if (GuiceContext.config.isAnnotationScanning())
{
graph.enableAnnotationInfo();
}
if (GuiceContext.config.isMethodInfo())
{
graph.enableMethodInfo();
}
if (GuiceContext.config.isIgnoreFieldVisibility())
{
graph.ignoreFieldVisibility();
}
if (GuiceContext.config.isIgnoreMethodVisibility())
{
graph.ignoreMethodVisibility();
}
if (GuiceContext.config.isVerbose())
{
graph.verbose();
}
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
private String[] getPackagesList()
{
Set strings = new LinkedHashSet<>();
Set exclusions = getLoader(IPackageContentsScanner.class, true, ServiceLoader.load(IPackageContentsScanner.class));
if (exclusions.iterator()
.hasNext())
{
for (IPackageContentsScanner exclusion : exclusions)
{
GuiceContext.log.log(Level.CONFIG, "Loading IPackageContentsScanner - " +
exclusion.getClass()
.getCanonicalName());
Set searches = exclusion.searchFor();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IPackageScanningContentsScanner - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
private String[] getBlacklistPackages()
{
Set strings = new LinkedHashSet<>();
Set exclusions = getLoader(IPackageBlackListScanner.class, true, ServiceLoader.load(IPackageBlackListScanner.class));
if (exclusions.iterator()
.hasNext())
{
for (IPackageBlackListScanner exclusion : exclusions)
{
GuiceContext.log.log(Level.CONFIG, "Loading IPackageContentsScanner - " +
exclusion.getClass()
.getCanonicalName());
Set searches = exclusion.exclude();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IPackageScanningContentsScanner - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* A set
*
* @param loaderType
* The service type
* @param
* The type
* @param dontInject
* Don't inject
*
* @return A set of them
*/
@SuppressWarnings("unchecked")
@NotNull
public Set getLoader(Class loaderType, @SuppressWarnings("unused") boolean dontInject, ServiceLoader serviceLoader)
{
if (!getAllLoadedServices().containsKey(loaderType))
{
Set loader = loaderToSetNoInjection(serviceLoader);
getAllLoadedServices().put(loaderType, loader);
}
return getAllLoadedServices().get(loaderType);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
private String[] getPathsList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IPathContentsScanner.class, true, ServiceLoader.load(IPathContentsScanner.class));
if (exclusions.iterator()
.hasNext())
{
for (IPathContentsScanner exclusion : exclusions)
{
GuiceContext.log.log(Level.CONFIG, "Loading IPathScanningContentsScanner - " +
exclusion.getClass()
.getCanonicalName());
Set searches = exclusion.searchFor();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IPathScanningContentsScanner - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
private String[] getPathsBlacklistList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IPathContentsBlacklistScanner.class, true, ServiceLoader.load(IPathContentsBlacklistScanner.class));
if (exclusions.iterator()
.hasNext())
{
for (IPathContentsBlacklistScanner exclusion : exclusions)
{
GuiceContext.log.log(Level.CONFIG, "Loading IPathContentsBlacklistScanner - " +
exclusion.getClass()
.getCanonicalName());
Set searches = exclusion.searchFor();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IPathContentsBlacklistScanner - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
@SuppressWarnings("unchecked")
private String[] getJarsWhiteList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IGuiceScanJarInclusions.class, true, ServiceLoader.load(IGuiceScanJarInclusions.class));
if (exclusions.iterator()
.hasNext())
{
for (IGuiceScanJarInclusions exclusion : exclusions)
{
Set searches = exclusion.includeJars();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IGuiceScanJarInclusions - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
@SuppressWarnings("unchecked")
private String[] getModulesWhiteList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IGuiceScanModuleInclusions.class, true, ServiceLoader.load(IGuiceScanModuleInclusions.class));
if (exclusions.iterator()
.hasNext())
{
for (IGuiceScanModuleInclusions exclusion : exclusions)
{
Set searches = exclusion.includeModules();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IGuiceScanModuleInclusions - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
@SuppressWarnings("unchecked")
private String[] getJarsBlacklistList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IGuiceScanJarExclusions.class, true, ServiceLoader.load(IGuiceScanJarExclusions.class));
if (exclusions.iterator()
.hasNext())
{
for (IGuiceScanJarExclusions exclusion : exclusions)
{
Set searches = exclusion.excludeJars();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IGuiceScanJarExclusions - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Returns a complete list of generic exclusions
*
* @return A string list of packages to be scanned
*/
@SuppressWarnings("unchecked")
private String[] getModulesBlacklistList()
{
Set strings = new TreeSet<>();
Set exclusions = getLoader(IGuiceScanModuleExclusions.class, true, ServiceLoader.load(IGuiceScanModuleExclusions.class));
if (exclusions.iterator()
.hasNext())
{
for (IGuiceScanModuleExclusions exclusion : exclusions)
{
Set searches = exclusion.excludeModules();
strings.addAll(searches);
}
GuiceContext.log.log(Level.FINE, "IGuiceScanModuleExclusions - " + strings.toString());
}
return strings.toArray(new String[0]);
}
/**
* Registers the quick scan files
*/
@SuppressWarnings("unchecked")
private Map quickScanFiles()
{
Map fileScans = new HashMap<>();
Set fileScanners = getLoader(IFileContentsScanner.class, true, ServiceLoader.load(IFileContentsScanner.class));
for (IFileContentsScanner fileScanner : fileScanners)
{
GuiceContext.log.log(Level.CONFIG, "Loading IFileContentsScanner - " +
fileScanner.getClass()
.getCanonicalName());
fileScans.putAll(fileScanner.onMatch());
}
return fileScans;
}
/**
* Method loadDefaultBinders ...
*
* @return List
*/
@SuppressWarnings("unchecked")
private List loadDefaultBinders()
{
Set preStartups = getLoader(IGuiceModule.class, true, ServiceLoader.load(IGuiceModule.class));
Set startups = new TreeSet<>(preStartups);
List output = new ArrayList<>();
for (IGuiceModule startup : startups)
{
GuiceContext.log.config("Loading IGuiceModule - " +
startup.getClass()
.getCanonicalName());
output.add(startup);
}
return output;
}
/**
* Returns the current classpath scanner
*
* @return Default processors count
*/
@SuppressWarnings("unused")
public ClassGraph getScanner()
{
return scanner;
}
/**
* Sets the classpath scanner
*
* @param scanner
* Sets the scanner to a specific instance
*/
@SuppressWarnings("unused")
public static void setScanner(ClassGraph scanner)
{
GuiceContext.instance().scanner = scanner;
}
/**
* Method loadPostStartups ...
*/
private void loadPostStartups()
{
Set startupSet = getLoader(IGuicePostStartup.class, ServiceLoader.load(IGuicePostStartup.class));
Map> postStartupGroups = new TreeMap<>();
for (IGuicePostStartup postStartup : startupSet)
{
Integer sortOrder = postStartup.sortOrder();
postStartupGroups.computeIfAbsent(sortOrder, k -> new TreeSet<>())
.add(postStartup);
}
postStartupGroups.forEach((key, value) ->
{
GuiceContext.get(JobService.class)
.registerJobPool("GuicePostStartupGroup", Executors.newWorkStealingPool(Runtime.getRuntime()
.availableProcessors()));
for (IGuicePostStartup postStartup : value)
{
GuiceContext.get(JobService.class)
.addJob("GuicePostStartupGroup", postStartup);
GuiceContext.log.config("Registering IGuicePostStartup - " +
postStartup
.getClass()
.getCanonicalName());
}
GuiceContext.get(JobService.class)
.waitForJob("GuicePostStartupGroup",defaultWaitTime,defaultWaitUnit);
GuiceContext.log.fine("Completed with Post Startups Key [" + key + "]");
});
}
/**
* Returns the Guice Config Instance
*
* @return The singleton Guice Config instance. Also available with @Inject
*/
public GuiceConfig> getConfig()
{
return GuiceContext.config;
}
/**
* Loads the service lists of post startup's for manual additions
*
* @return The list of guice post startups
*/
public @NotNull
Set loadPostStartupServices()
{
return getLoader(IGuicePostStartup.class, ServiceLoader.load(IGuicePostStartup.class));
}
/**
* A set
*
* @param loaderType
* The service type
* @param
* The type
*
* @return A set of them
*/
@SuppressWarnings("unchecked")
@NotNull
public Set getLoader(Class loaderType, ServiceLoader serviceLoader)
{
if (!getAllLoadedServices().containsKey(loaderType))
{
Set loader;
if (GuiceContext.buildingInjector)
{
loader = loaderToSetNoInjection(serviceLoader);
}
else
{
loader = loaderToSet(serviceLoader);
}
getAllLoadedServices().put(loaderType, loader);
}
return getAllLoadedServices().get(loaderType);
}
/**
* Method getAllLoadedServices returns the allLoadedServices of this GuiceContext object.
*
* A list of all the loaded singleton sets
*
* @return the allLoadedServices (type Map Class, Set ) of this GuiceContext object.
*/
@NotNull
public static Map getAllLoadedServices()
{
return allLoadedServices;
}
}