/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.internal.providers; import com.jcwhatever.nucleus.BukkitPlugin; import com.jcwhatever.nucleus.Nucleus; import com.jcwhatever.nucleus.internal.NucMsg; import com.jcwhatever.nucleus.managed.scheduler.Scheduler; import com.jcwhatever.nucleus.providers.Provider; import com.jcwhatever.nucleus.utils.DependencyRunner; import com.jcwhatever.nucleus.utils.DependencyRunner.DependencyStatus; import com.jcwhatever.nucleus.utils.DependencyRunner.IDependantRunnable; import com.jcwhatever.nucleus.utils.DependencyRunner.IFinishHandler; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.file.FileUtils.DirectoryTraversal; import com.jcwhatever.nucleus.utils.modules.ClassLoadMethod; import com.jcwhatever.nucleus.utils.modules.IModuleInfo; import com.jcwhatever.nucleus.utils.modules.JarModuleLoader; import com.jcwhatever.nucleus.utils.observer.future.FutureSubscriber; import com.jcwhatever.nucleus.utils.observer.future.IFuture.FutureStatus; import javax.annotation.Nullable; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.JarFile; /** * Loads provider modules from /plugins/NucleusFramework/providers folder */ public final class InternalProviderLoader extends JarModuleLoader<Provider> { private final InternalProviderManager _manager; private final File _folder; private final DependencyRunner<IDependantRunnable> _depend = new DependencyRunner<IDependantRunnable>(Nucleus.getPlugin()); // keyed to module class name private final Map<String, InternalProviderModuleInfo> _moduleInfoMap = new HashMap<>(10); /** * Constructor. */ public InternalProviderLoader(InternalProviderManager providerManager) { super(Nucleus.getPlugin(), Provider.class); PreCon.notNull(providerManager); _manager = providerManager; _folder = new File(Nucleus.getPlugin().getDataFolder(), "providers"); } @Override public File getModuleFolder() { return _folder; } @Override public DirectoryTraversal getDirectoryTraversal() { return DirectoryTraversal.NONE; } @Override public void loadModules() { if (!getModuleFolder().exists()) { Scheduler.runTaskLater(Nucleus.getPlugin(), new Runnable() { @Override public void run() { ((BukkitPlugin) Nucleus.getPlugin()).notifyProvidersReady(); } }); return; } super.loadModules(); _depend.onFinish(new IFinishHandler<IDependantRunnable>() { @Override public void onFinish(List<IDependantRunnable> notRun) { _manager.setLoading(false); _manager.enableProviders() .onSuccess(new FutureSubscriber() { @Override public void on(FutureStatus status, @Nullable CharSequence message) { ((BukkitPlugin)Nucleus.getPlugin()).notifyProvidersReady(); } }); } }); _manager.setLoading(true); _depend.start(); } @Override protected ClassLoadMethod getLoadMethod(File file) { return ClassLoadMethod.DIRECT; } @Override protected String getModuleClassName(JarFile jarFile) { InternalProviderModuleInfo info = new InternalProviderModuleInfo(jarFile); if (!info.isValid()) return null; _moduleInfoMap.put(info.getModuleClassName(), info); return info.getModuleClassName(); } @Nullable @Override protected IModuleInfo createModuleInfo(Provider moduleInstance) { return _moduleInfoMap.get(moduleInstance.getClass().getCanonicalName()); } @Nullable @Override protected Provider instantiateModule(Class<Provider> clazz) { final Provider instance; final InternalProviderModuleInfo info = _moduleInfoMap.get(clazz.getCanonicalName()); if (info == null) return null; try { Constructor<Provider> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); instance = constructor.newInstance(); } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); return null; } instance.setInfo(info); _depend.add(new DependantRunnable(instance, info)); return instance; } private class DependantRunnable implements IDependantRunnable { final Provider provider; final InternalProviderModuleInfo info; DependantRunnable(Provider provider, InternalProviderModuleInfo info) { this.provider = provider; this.info = info; } @Override public DependencyStatus getDependencyStatus() { boolean dependsReady = info.isDependsReady(); boolean softReady = info.isSoftDependsReady(); if (dependsReady && softReady) { return DependencyStatus.READY; } else { return dependsReady ? DependencyStatus.REQUIRED_READY : DependencyStatus.NOT_READY; } } @Override public void run() { if (!_manager.addProvider(provider)) { NucMsg.debug("Failed to add service provider: '{0}'", provider.getInfo().getName()); removeModule(provider.getInfo().getName()); } } } }