package fi.otavanopisto.pyramus.plugin; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.sonatype.aether.artifact.Artifact; import org.sonatype.aether.collection.DependencyCollectionException; import org.sonatype.aether.graph.Exclusion; import org.sonatype.aether.repository.RemoteRepository; import org.sonatype.aether.resolution.ArtifactResult; import org.sonatype.aether.resolution.DependencyResolutionException; import org.sonatype.aether.util.artifact.ArtifacIdUtils; import org.sonatype.aether.util.artifact.DefaultArtifact; import sun.misc.Service; import fi.internetix.smvc.logging.Logging; import fi.otavanopisto.pyramus.domainmodel.plugins.PluginRepository; import fi.otavanopisto.pyramus.plugin.maven.MavenClient; import fi.otavanopisto.pyramus.plugin.scheduler.ScheduledPluginDescriptor; import fi.otavanopisto.pyramus.plugin.scheduler.ScheduledPluginTask; import fi.otavanopisto.pyramus.plugin.scheduler.ScheduledTaskInterval; /** The class responsible for managing plugins. * */ @SuppressWarnings("restriction") public class PluginManager { /** Returns the (singleton) instance of the plugin manager. * * @return The (singleton) instance of the plugin manager. */ public static final synchronized PluginManager getInstance() { return INSTANCE; } /** Initializes the plugin manager. Call this before * any other methods. * * @param parentClassLoader The parent class loader of the plugin manager. * @param pluginRepositories The URL:s of the repositories containing the plugins. * @return The plugin manager instance. */ public static final synchronized PluginManager initialize(ClassLoader parentClassLoader, List<PluginRepository> pluginRepositories) { if (INSTANCE != null) throw new PluginManagerException("Plugin manger is already initialized"); INSTANCE = new PluginManager(parentClassLoader, pluginRepositories); return INSTANCE; } private static PluginManager INSTANCE = null; PluginManager(ClassLoader parentClassLoader, List<PluginRepository> pluginRepositories) { this.libraryLoader = new LibraryLoader(parentClassLoader); mavenClient = new MavenClient(getPluginDirectory(), System.getProperty("pyramus.workspace")); // Maven Central repository is always present mavenClient.addRepository(new RemoteRepository("central", "default", "http://repo.maven.apache.org/maven2")); for (PluginRepository repository : pluginRepositories) { mavenClient.addRepository(repository.getRepositoryId(), repository.getUrl()); } } /** Removes a plugin repository from the plugin manager. * * @param url The URL of the repository to remove. */ public void removeRepository(String url) { mavenClient.removeRepository(url); } private File getPluginDirectory() { // TODO: Proper error handling String absoluteParent = new File(".").getAbsolutePath(); absoluteParent = absoluteParent.substring(0, absoluteParent.length() - 1); File parentDirectory = new File(absoluteParent); if (parentDirectory.exists()) { File pluginDirectory = new File(parentDirectory, "PyramusPlugins"); if (pluginDirectory.exists()) { if (pluginDirectory.canRead() && pluginDirectory.canWrite()) { return pluginDirectory; } else { throw new PluginManagerException("Cannot read or write into plugin directory '" + pluginDirectory +"'"); } } else { if (parentDirectory.canWrite()) { if (!pluginDirectory.mkdir()) { throw new PluginManagerException("Failed to create new plugin directory under parent directory '" + parentDirectory + "'"); } else { return pluginDirectory; } } else { throw new PluginManagerException("Unable to create new plugin directory. Parent directory '" + parentDirectory + "' is write protected"); } } } else { throw new PluginManagerException("Plugins parent directory '" + parentDirectory + "' does not exist"); } } /** Loads a plugin using Maven. * * @param groupId The Maven group ID of the plugin to load. * @param artifactId The Maven artifact ID of the plugin to load. * @param version The version of the plugin to load. */ public void loadPlugin(String groupId, String artifactId, String version) { // String groupId = loadInfo.getGroupId(); // String artifactId = loadInfo.getArtifactId(); // String version = loadInfo.getVersion(); // loadedPluginLibraryInfos.add(loadInfo); try { Artifact libraryArtifact = new DefaultArtifact(groupId, artifactId, "jar", version); List<Exclusion> applicationProvidedArtifacts = new ArrayList<>(); List<ArtifactResult> resolvedDependencies = mavenClient.resolveDependencies(libraryArtifact, "compile", applicationProvidedArtifacts); for (ArtifactResult resolvedDependency : resolvedDependencies) { if (resolvedDependency.isResolved()) { File artifactFile = mavenClient.getArtifactJarFile(resolvedDependency.getArtifact()); if (artifactFile.isDirectory()) { Logging.logInfo("Loading " + ArtifacIdUtils.toId(resolvedDependency.getArtifact()) + " plugin folder: " + artifactFile); try { libraryLoader.loadClassPath(artifactFile.toURI().toURL()); } catch (Exception e) { Logging.logException("Error occurred while loading plugin folder " + artifactFile, e); } } else { Logging.logInfo("Loading " + ArtifacIdUtils.toId(resolvedDependency.getArtifact()) + " plugin library jar: " + artifactFile); libraryLoader.loadJar(artifactFile); } } else { Logging.logError("Could not resolve " + ArtifacIdUtils.toId(libraryArtifact) + " -plugin dependency: " + ArtifacIdUtils.toId(resolvedDependency.getArtifact())); } } } catch (DependencyCollectionException | DependencyResolutionException e) { throw new PluginManagerException(e); } } /** Returns <code>true</code> if the given Maven artifact * is loaded as a plugin, and <code>false</code> otherwise. * * @param groupId The Maven group ID of the plugin to check for. * @param artifactId The Maven artifact ID of the plugin to check for. * @param version The version of the plugin to check for. * @return <code>true</code> if the given Maven artifact is * loaded as a plugin, and <code>false</code> otherwise. */ public boolean isLoaded(String groupId, String artifactId, String version) { try { Artifact libraryArtifact = new DefaultArtifact(groupId, artifactId, "jar", version); File jarFile = mavenClient.getArtifactJarFile(libraryArtifact); // ArtifactDescriptorResult descriptorResult = mavenClient.describeArtifact(groupId, artifactId, version); // File jarFile = mavenClient.getArtifactJarFile(descriptorResult.getArtifact()); return libraryLoader.isJarLoaded(jarFile); } catch (Exception e) { return false; } } /** Register the loaded plugins. * */ public void registerPlugins() { Iterator<PluginDescriptor> pluginDescriptors = Service.providers(PluginDescriptor.class, libraryLoader.getPluginsClassLoader()); while (pluginDescriptors.hasNext()) { PluginDescriptor pluginDescriptor = pluginDescriptors.next(); registerPlugin(pluginDescriptor); } } /** Returns the class loader that loads the plugins. * * @return the class loader that loads the plugins. */ public ClassLoader getPluginsClassLoader() { return libraryLoader.getPluginsClassLoader(); } /** Returns the currently loaded plugins. * * @return the currently loaded plugins. */ public synchronized List<PluginDescriptor> getPlugins() { return plugins; } /** Register a loaded plugin. * * @param plugin The plugin to register. */ public synchronized void registerPlugin(PluginDescriptor plugin) { for (PluginDescriptor pluginDescriptor : plugins) { if (pluginDescriptor.getName().equals(plugin.getName())) return; } plugins.add(plugin); } public List<ScheduledPluginTask> getScheduledTasks(ScheduledTaskInterval internal) { List<ScheduledPluginTask> result = new ArrayList<>(); for (ScheduledPluginDescriptor sceduledPlugin : getScheduledPlugins()) { List<ScheduledPluginTask> tasks = sceduledPlugin.getScheduledTasks(); if (tasks != null) { for (ScheduledPluginTask task : tasks) { if (internal.equals(task.getInternal())) { result.add(task); } } } } return result; } public List<ScheduledPluginDescriptor> getScheduledPlugins() { List<ScheduledPluginDescriptor> result = new ArrayList<>(); for (PluginDescriptor plugin : getPlugins()) { if (plugin instanceof ScheduledPluginDescriptor) { result.add((ScheduledPluginDescriptor) plugin); } } return Collections.unmodifiableList(result); } public List<CustomLoginScreenPlugin> getCustomLoginScreenPlugins() { List<CustomLoginScreenPlugin> result = new ArrayList<>(); for (PluginDescriptor plugin : getPlugins()) { if (plugin instanceof CustomLoginScreenPlugin) { result.add((CustomLoginScreenPlugin) plugin); } } return Collections.unmodifiableList(result); } public String getCustomLoginScreen(String contextType, String contextId) { List<CustomLoginScreenPlugin> customLoginScreenPlugins = getCustomLoginScreenPlugins(); if (customLoginScreenPlugins != null) { for (CustomLoginScreenPlugin customLoginScreenPlugin : customLoginScreenPlugins) { String loginFtl = customLoginScreenPlugin.getContextLoginFtl(contextType, contextId); if (StringUtils.isNotBlank(loginFtl)) { return loginFtl; } } } return null; } // private List<Exclusion> applicationProvidedArtifacts = new ArrayList<>(); private LibraryLoader libraryLoader; private MavenClient mavenClient; private List<PluginDescriptor> plugins = new ArrayList<>(); }