net.minecrell.quartz.plugin.QuartzPluginManager.java Source code

Java tutorial

Introduction

Here is the source code for net.minecrell.quartz.plugin.QuartzPluginManager.java

Source

/*
 * Quartz
 * Copyright (c) 2015, Minecrell <https://github.com/Minecrell>
 *
 * Based on Sponge and SpongeAPI, licensed under the MIT License (MIT).
 * Copyright (c) SpongePowered.org <http://www.spongepowered.org>
 * Copyright (c) contributors
 *
 * 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 net.minecrell.quartz.plugin;

import static java.util.Objects.requireNonNull;

import com.google.common.base.Optional;
import net.minecraft.launchwrapper.Launch;
import net.minecrell.quartz.Quartz;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.plugin.PluginManager;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class QuartzPluginManager implements PluginManager {

    private static final String PLUGIN_DESCRIPTOR = Type.getDescriptor(Plugin.class);

    private final Quartz quartz;
    private final Map<String, PluginContainer> plugins = new HashMap<>();
    private final Map<Object, PluginContainer> pluginInstances = new IdentityHashMap<>();

    @Inject
    public QuartzPluginManager(Quartz quartz) {
        this.quartz = requireNonNull(quartz, "quartz");
        plugins.put(quartz.getId(), quartz);
        pluginInstances.put(quartz, quartz);
    }

    public void loadPlugins() throws IOException {
        try (DirectoryStream<Path> dir = Files.newDirectoryStream(quartz.getPluginsDirectory(), "*.jar")) {
            for (Path jar : dir) {
                String pluginClassName = null;

                try (ZipFile zip = new ZipFile(jar.toFile())) {
                    Enumeration<? extends ZipEntry> entries = zip.entries();
                    while (entries.hasMoreElements()) {
                        ZipEntry entry = entries.nextElement();
                        if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
                            continue;
                        }

                        try (InputStream in = zip.getInputStream(entry)) {
                            if ((pluginClassName = findPlugin(in)) != null) {
                                break;
                            }
                        }
                    }
                } catch (IOException e) {
                    quartz.getLogger().error("Failed to load plugin JAR: " + jar, e);
                    continue;
                }

                // Load the plugin
                if (pluginClassName != null) {
                    try {
                        Launch.classLoader.addURL(jar.toUri().toURL());
                        Class<?> pluginClass = Class.forName(pluginClassName);
                        QuartzPluginContainer container = new QuartzPluginContainer(pluginClass);
                        plugins.put(container.getId(), container);
                        pluginInstances.put(container.getInstance(), container);
                        quartz.getGame().getEventManager().register(container, container.getInstance());

                        quartz.getLogger().info("Loaded plugin: {} (from {})", container.getName(), jar);
                    } catch (Throwable e) {
                        quartz.getLogger()
                                .error("Failed to load plugin: " + pluginClassName + " (from " + jar + ')', e);
                    }
                }
            }
        }
    }

    @Nullable
    private String findPlugin(InputStream in) throws IOException {
        ClassReader reader = new ClassReader(in);
        ClassNode classNode = new ClassNode();
        reader.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);

        if (classNode.visibleAnnotations != null) {
            for (AnnotationNode node : classNode.visibleAnnotations) {
                if (node.desc.equals(PLUGIN_DESCRIPTOR)) {
                    return classNode.name.replace('/', '.');
                }
            }
        }

        return null;
    }

    @Override
    public Optional<PluginContainer> fromInstance(Object instance) {
        requireNonNull(instance, "instance");

        if (instance instanceof QuartzPluginContainer) {
            return Optional.of((PluginContainer) instance);
        }

        return Optional.fromNullable(pluginInstances.get(instance));
    }

    @Override
    public Optional<PluginContainer> getPlugin(String id) {
        return Optional.fromNullable(plugins.get(id));
    }

    @Override
    public Logger getLogger(PluginContainer plugin) {
        return ((QuartzPluginContainer) plugin).getLogger();
    }

    @Override
    public Collection<PluginContainer> getPlugins() {
        return Collections.unmodifiableCollection(plugins.values());
    }

    @Override
    public boolean isLoaded(String id) {
        return plugins.containsKey(id);
    }

}