001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.plugins; 019 020import io.stallion.boot.StallionRunAction; 021import io.stallion.plugins.javascript.JsPluginEngine; 022 023import io.stallion.plugins.javascript.TestResults; 024import io.stallion.services.Log; 025import org.apache.commons.lang3.StringUtils; 026import org.apache.xbean.classloader.JarFileClassLoader; 027 028import java.io.File; 029import java.net.URL; 030import java.util.ArrayList; 031import java.util.List; 032import java.util.Map; 033import java.util.jar.JarFile; 034import java.util.jar.Manifest; 035 036import static io.stallion.Context.*; 037import static io.stallion.utils.Literals.*; 038 039/** 040 * Registry Service that loads plugins, boots them, and keeps track of them. 041 * 042 */ 043public class PluginRegistry { 044 045 046 private JarFileClassLoader classLoader; 047 048 private Map<String, StallionJavaPlugin> javaPluginByName = map(); 049 private Map<String, JsPluginEngine> jsPlugins = map(); 050 051 private static PluginRegistry _instance; 052 053 public static PluginRegistry instance() { 054 055 return _instance; 056 } 057 058 public static PluginRegistry loadWithJavaPlugins(String targetPath) { 059 if (_instance == null) { 060 _instance = new PluginRegistry(); 061 _instance.loadJarPlugins(targetPath); 062 } 063 return _instance; 064 } 065 066 public static PluginRegistry loadWithJavaPlugins(String targetPath, StallionJavaPlugin ...extraPlugins) { 067 if (_instance == null) { 068 _instance = new PluginRegistry(); 069 _instance.loadJarPlugins(targetPath); 070 for (StallionJavaPlugin plugin: extraPlugins) { 071 _instance.loadPluginFromBooter(plugin); 072 } 073 } 074 return _instance; 075 } 076 077 PluginRegistry() { 078 //new JarFileClassLoader() 079 this.classLoader = new JarFileClassLoader( 080 "PluginClassLoader" + System.currentTimeMillis(), 081 new URL[]{}, 082 PluginRegistry.class.getClassLoader() 083 ); 084 } 085 086 /* 087 public static PluginRegistry load() { 088 return PluginRegistry.load(new ArrayList<StallionPlugin>()); 089 } 090 091 public static PluginRegistry load(List<StallionPlugin> extraPlugins) { 092 _instance = new PluginRegistry(); 093 try { 094 095 if (extraPlugins != null) { 096 for(StallionPlugin booter: extraPlugins) { 097 _instance.loadPluginFromBooter(booter); 098 } 099 } 100 _instance.load(true); 101 } catch (Exception e) { 102 throw new RuntimeException(e); 103 } 104 return _instance; 105 } 106 */ 107 108 public static void shutdown() { 109 if (_instance != null) { 110 for (JsPluginEngine engine: _instance.jsPlugins.values()) { 111 engine.shutdown(); 112 } 113 for (StallionJavaPlugin plugin: _instance.getJavaPluginByName().values()) { 114 plugin.shutdown(); 115 } 116 } 117 _instance = null; 118 } 119 120 121 122 // public void load(Boolean shouldWatchFiles) throws Exception { 123 // loadPlugins(shouldWatchFiles); 124 //} 125 126 public List<StallionRunAction> getAllPluginDefinedStallionRunActions() { 127 List<StallionRunAction> actions = list(); 128 for (StallionJavaPlugin plugin: getJavaPluginByName().values()) { 129 actions.addAll(plugin.getActions()); 130 } 131 return actions; 132 } 133 134 public void loadAndRunJavascriptPlugins() { 135 loadAndRunJavascriptPlugins(true); 136 } 137 138 public void loadAndRunJavascriptPlugins(Boolean shouldWatchFiles) { 139 try { 140 doLoadAndRunJavascriptPlugins(shouldWatchFiles); 141 } catch (Exception e) { 142 throw new RuntimeException(e); 143 } 144 } 145 146 private void doLoadAndRunJavascriptPlugins(Boolean shouldWatchFiles) throws Exception { 147 148 149 // Load the main javascript file, if it exists 150 String jsMainPath = settings().getTargetFolder() + "/js/main.js"; 151 File jsMain = new File(jsMainPath); 152 if (jsMain.exists() && jsMain.isFile()) { 153 JsPluginEngine engine = new JsPluginEngine(shouldWatchFiles); 154 jsPlugins.put("main.js", engine); 155 engine.loadJavascript("main.js", jsMainPath); 156 } 157 158 159 String path = settings().getTargetFolder() + "/plugins"; 160 161 File folder = new File(path); 162 if (!folder.exists() || !folder.isDirectory()) { 163 return; 164 } 165 File[] children = folder.listFiles(); 166 if (children != null) { 167 for (File child : children) { 168 if (child.isDirectory()) { 169 170 loadScriptPlugin(child.getAbsolutePath(), child.getName(), shouldWatchFiles); 171 } 172 } 173 } 174 175 } 176 177 private void loadScriptPlugin(String folderPath, String pluginName, Boolean shouldWatchFiles) throws Exception { 178 //PluginDefinition definition = new Toml().parse(pluginTomlFile).to(PluginDefinition.class); 179 Log.info("Load plugin {0} name:{1}", folderPath, pluginName); 180 181 String jsPath = folderPath + "/plugin.js"; 182 File jsFile = new File(jsPath); 183 if (jsFile.exists() && jsFile.isFile()) { 184 JsPluginEngine engine = new JsPluginEngine(shouldWatchFiles); 185 jsPlugins.put(pluginName, engine); 186 engine.loadJavascript(pluginName, jsPath); 187 188 } 189 190 191 } 192 193 public void loadJarPlugins(String targetPath) { 194 try { 195 doLoadJarPlugins(targetPath); 196 } catch (Exception e) { 197 throw new RuntimeException(e); 198 } 199 200 } 201 private void doLoadJarPlugins(String targetPath) throws Exception { 202 String jarFolderPath = targetPath + "/jars"; 203 Log.fine("Loading jars at {0}", jarFolderPath); 204 File jarFolder = new File(jarFolderPath); 205 if (!jarFolder.exists() || !jarFolder.isDirectory()) { 206 Log.fine("No jar folder exists at {0}. No jar plugins will be loaded.", jarFolderPath); 207 return; 208 } 209 File[] files = jarFolder.listFiles(); 210 ArrayList<URL> urls = new ArrayList<URL>(); 211 for (int i = 0; i < files.length; i++) { 212 if (files[i].getName().endsWith(".jar")) { 213 //urls.add(files[i].toURL()); 214 Log.fine("Loading plugin jar {0}", files[i].toURI()); 215 urls.add(files[i].toURI().toURL()); 216 } 217 } 218 219 String pluginBooterClass = ""; 220 221 for (URL jarUrl: urls) { 222 // Add jars to the class loader 223 getClassLoader().addURL(jarUrl); 224 } 225 226 for (URL jarUrl: urls) { 227 // Load the booter class 228 Manifest m = new JarFile(jarUrl.getFile()).getManifest(); 229 Log.finer("Found manifest for jar {0}", jarUrl); 230 if (!StringUtils.isEmpty(m.getMainAttributes().getValue("pluginBooterClass"))) { 231 pluginBooterClass = m.getMainAttributes().getValue("pluginBooterClass"); 232 } 233 if (empty(pluginBooterClass)) { 234 continue; 235 } 236 Log.fine("Load plugin class {0} from jar={1}", pluginBooterClass, jarUrl); 237 Class booterClass = getClassLoader().loadClass(pluginBooterClass); 238 Log.finer("Booter class was loaded from: {0} ", booterClass.getProtectionDomain().getCodeSource().getLocation()); 239 StallionJavaPlugin booter = (StallionJavaPlugin)booterClass.newInstance(); 240 loadPluginFromBooter(booter); 241 } 242 } 243 244 /** 245 * Boot all jar plugins that have already been loaded. 246 * 247 */ 248 public void bootJarPlugins() { 249 for(StallionJavaPlugin plugin: getJavaPluginByName().values()) { 250 try { 251 plugin.boot(); 252 } catch (Exception e) { 253 throw new RuntimeException(e); 254 } 255 } 256 } 257 258 public void loadPluginFromBooter(StallionJavaPlugin booter) { 259 Log.info("Load plugin {0} from class {1}", booter.getPluginName(), booter.getClass().getName()); 260 if (getJavaPluginByName().containsKey(booter.getPluginName())) { 261 Log.warn("Plugin already loaded, skipping {0}", booter.getPluginName()); 262 return; 263 } 264 booter.setPluginRegistry(this); 265 getJavaPluginByName().put(booter.getPluginName(), booter); 266 } 267 268 public List<TestResults> runJsTests(String plugin, String jsFile) { 269 return jsPlugins.get(plugin).runTestsInFile(jsFile); 270 } 271 272 public JsPluginEngine getEngine(String plugin) { 273 return jsPlugins.getOrDefault(plugin, null); 274 } 275 276 277 public JarFileClassLoader getClassLoader() { 278 return classLoader; 279 } 280 281 public void setClassLoader(JarFileClassLoader classLoader) { 282 this.classLoader = classLoader; 283 } 284 285 286 public Map<String, StallionJavaPlugin> getJavaPluginByName() { 287 return javaPluginByName; 288 } 289}