PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/mpv5/pluginhandling/MPPLuginLoader.java

http://mp-rechnungs-und-kundenverwaltung.googlecode.com/
Java | 372 lines | 263 code | 29 blank | 80 comment | 32 complexity | b4e719a4b5710246a928cb50b8c435b2 MD5 | raw file
Possible License(s): LGPL-3.0, Apache-2.0, GPL-3.0, GPL-2.0, AGPL-3.0, JSON, BSD-3-Clause
  1. /*
  2. * This file is part of YaBS.
  3. *
  4. * YaBS is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * YaBS is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with YaBS. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package mpv5.pluginhandling;
  18. import java.awt.Component;
  19. import java.awt.Image;
  20. import java.awt.event.ActionEvent;
  21. import java.awt.event.ActionListener;
  22. import java.awt.event.MouseAdapter;
  23. import java.awt.event.MouseEvent;
  24. import java.awt.image.BufferedImage;
  25. import java.io.File;
  26. import java.io.FileNotFoundException;
  27. import java.net.URL;
  28. import java.net.URLClassLoader;
  29. import java.net.URLStreamHandlerFactory;
  30. import java.util.ArrayList;
  31. import java.util.Arrays;
  32. import java.util.List;
  33. import java.util.Vector;
  34. import java.util.logging.Level;
  35. import java.util.logging.Logger;
  36. import javax.imageio.ImageIO;
  37. import javax.swing.Icon;
  38. import javax.swing.ImageIcon;
  39. import javax.swing.JComponent;
  40. import javax.swing.JLabel;
  41. import javax.swing.JMenuItem;
  42. import javax.swing.JPopupMenu;
  43. import mpv5.Main;
  44. import mpv5.db.common.Context;
  45. import mpv5.db.common.NodataFoundException;
  46. import mpv5.db.common.QueryCriteria;
  47. import mpv5.db.common.DatabaseObject;
  48. import mpv5.db.common.DatabaseObjectModifier;
  49. import mpv5.db.common.QueryHandler;
  50. import mpv5.globals.Constants;
  51. import mpv5.globals.LocalSettings;
  52. import mpv5.globals.Messages;
  53. import mpv5.logging.Log;
  54. import mpv5.ui.dialogs.Popup;
  55. import mpv5.ui.frames.MPView;
  56. import mpv5.utils.images.MPIcon;
  57. /**
  58. *
  59. * This class loads plugins from the database, and utilises caching of the plugin files.
  60. */
  61. public class MPPLuginLoader {
  62. public static String pluginSignature = LocalSettings.getProperty(LocalSettings.CACHE_DIR) + File.separator + "%%filename%%-mp5p.jar";
  63. public static List<DatabaseObjectModifier> registeredModifiers = new Vector<DatabaseObjectModifier>();
  64. public static Image getDefaultPluginImage() {
  65. BufferedImage img = null;
  66. try {
  67. img = ImageIO.read(MPPLuginLoader.class.getResource("/mpv5/resources/images/48/blockdevice.png"));
  68. } catch (Exception e) {
  69. Log.Debug(e);
  70. }
  71. return img;
  72. }
  73. public static Image getErrorImage() {
  74. BufferedImage img = null;
  75. try {
  76. img = ImageIO.read(MPPLuginLoader.class.getResource("/mpv5/resources/images/48/messagebox_question.png"));
  77. } catch (Exception e) {
  78. Log.Debug(e);
  79. }
  80. return img;
  81. }
  82. public static void importPlugin(String name, File file) throws FileNotFoundException {
  83. mpv5.YabsViewProxy.instance().setWaiting(true);
  84. try {
  85. MP5Plugin pl = new MPPLuginLoader().checkPlugin(file);
  86. try {
  87. Thread.sleep(10000);
  88. } catch (InterruptedException ex) {
  89. Logger.getLogger(MPPLuginLoader.class.getName()).log(Level.SEVERE, null, ex);
  90. }
  91. if (pl != null) {
  92. String s = name;
  93. if (s != null) {
  94. String filename = QueryHandler.instanceOf().clone(Context.getFiles()).insertFile(file);
  95. Plugin p = new Plugin();
  96. p.setDescription(s);
  97. p.setCname("Plugin: " + pl.getName());
  98. p.setFilename(filename);
  99. p.save();
  100. }
  101. }
  102. } catch (FileNotFoundException fileNotFoundException) {
  103. Popup.error(mpv5.Main.getApplication().getMainFrame(), "Your database seems slow, try again :-/");
  104. } finally {
  105. mpv5.YabsViewProxy.instance().setWaiting(false);
  106. }
  107. }
  108. /**
  109. * Loads all plugins which are assigned to the current logged in user from database or cache dir<br/>
  110. * does NOT call plugin.load()
  111. * @return An array of instantiated plugins
  112. */
  113. public static MP5Plugin[] getPlugins() {
  114. ArrayList<MP5Plugin> list = null;
  115. try {
  116. list = new ArrayList<MP5Plugin>();
  117. ArrayList<File> jars = new ArrayList<File>();
  118. QueryCriteria criterias = new QueryCriteria("usersids", mpv5.db.objects.User.getCurrentUser().__getIDS());
  119. try {
  120. ArrayList<UserPlugin> data = DatabaseObject.getObjects(Context.getPluginsToUsers(), criterias);
  121. for (int i = 0; i < data.size(); i++) {
  122. UserPlugin up = data.get(i);
  123. Plugin o = ((Plugin) DatabaseObject.getObject(Context.getPlugins(), up.__getPluginsids()));
  124. Log.Debug(MPPLuginLoader.class, "Found Plugin: " + o);
  125. if (!isCachedPlugin(o.__getFilename())) {
  126. Log.Debug(MPPLuginLoader.class, "Caching plugin: " + pluginSignature.replace("%%filename%%", o.__getFilename()));
  127. jars.add(QueryHandler.instanceOf().clone(Context.getFiles()).retrieveFile(o.__getFilename(), new File(pluginSignature.replace("%%filename%%", o.__getFilename()))));
  128. } else {
  129. Log.Debug(MPPLuginLoader.class, "Using cached plugin: " + pluginSignature.replace("%%filename%%", o.__getFilename()));
  130. jars.add(new File(pluginSignature.replace("%%filename%%", o.__getFilename())));
  131. }
  132. }
  133. } catch (NodataFoundException ex) {
  134. Log.Debug(MPPLuginLoader.class, "No plugins found: " + ex.getMessage());
  135. }
  136. for (int i = 0; i < jars.size(); i++) {
  137. File x = jars.get(i);
  138. MP5Plugin c = checkPlugin(x);
  139. if (c != null) {
  140. list.add(c);
  141. }
  142. }
  143. } catch (Exception e) {
  144. Popup.error(e);
  145. Log.Debug(e);
  146. }
  147. return list.toArray(new MP5Plugin[0]);
  148. }
  149. /**
  150. * Checks if the plugin is already cached
  151. * @param filename
  152. * @return false if the file DOES NOT EXIST in the plugin cache directory, true otherwise
  153. */
  154. public static boolean isCachedPlugin(String filename) {
  155. File f = new File(pluginSignature.replace("%%filename%%", filename));
  156. Log.Debug(MPPLuginLoader.class, "Checking cache for " + filename);
  157. return f.exists() && f.canRead() && (checkPlugin(f) != null);
  158. }
  159. /**
  160. * Checks if the given file is a valid plugin
  161. * @param pluginCandidate
  162. * @return the plugin if <code>Constants.PLUGIN_LOAD_CLASS<code/> could be instantiated from this file
  163. */
  164. public static MP5Plugin checkPlugin(final File pluginCandidate) {
  165. try {
  166. URL[] urls = {new URL("jar:file:" + pluginCandidate + "!/")};
  167. URLClassLoader loader = new AddURLClassLoader(urls, Main.getApplication().getClass().getClassLoader());
  168. Class c = loader.loadClass(Constants.PLUGIN_LOAD_CLASS);
  169. Object o = c.newInstance();
  170. return (MP5Plugin) o;
  171. } catch (Exception malformedURLException) {
  172. Log.Debug(malformedURLException);
  173. Popup.error(malformedURLException);
  174. return null;
  175. }
  176. }
  177. /**
  178. * A nifty classloader
  179. */
  180. public static class AddURLClassLoader extends URLClassLoader {
  181. public AddURLClassLoader(URL[] urls, ClassLoader parent) {
  182. super(urls, parent);
  183. }
  184. public AddURLClassLoader(URL[] urls) {
  185. super(urls);
  186. }
  187. public AddURLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
  188. super(urls, parent, factory);
  189. }
  190. @Override
  191. public void addURL(URL url) {
  192. super.addURL(url);
  193. }
  194. }
  195. /**
  196. *
  197. * @param file
  198. * @return
  199. */
  200. public MP5Plugin getPlugin(File file) {
  201. return checkPlugin(file);
  202. }
  203. /**
  204. * Load all queued plugins
  205. */
  206. public static void loadPlugins() throws Exception, Throwable {
  207. for (int i = 0; i < pluginstoBeLoaded.size(); i++) {
  208. MP5Plugin mP5Plugin = pluginstoBeLoaded.get(i);
  209. loadPlugin(mP5Plugin);
  210. }
  211. }
  212. /**
  213. * Queues plugins to be loaded after the main Frame is showing.</br>
  214. * Adding plugins AFTER the main Frame is constructed will result in nothing.</br>
  215. * Use {@link loadPlugin(MP5Plugin)} instead.
  216. * @param plugins
  217. */
  218. public static void queuePlugins() {
  219. pluginstoBeLoaded.addAll(Arrays.asList(getPlugins()));
  220. }
  221. private static ArrayList<MP5Plugin> pluginstoBeLoaded = new ArrayList<MP5Plugin>();
  222. /**
  223. * Unloads the plugin and notifies the main view about the unload
  224. * @param mP5Plugin
  225. */
  226. public static void unLoadPlugin(MP5Plugin mP5Plugin) {
  227. mP5Plugin.unload();
  228. loadedPlugs.remove(mP5Plugin.getUID());
  229. // Component[] c = mpv5.YabsViewProxy.instance().getIdentifierView().getPluginIcons().getComponents();
  230. // for (int i = 0; i < c.length; i++) {
  231. // Component component = c[i];
  232. // if (((JLabel) component).getToolTipText().contains(String.valueOf(mP5Plugin.getUID()))) {
  233. //// mpv5.YabsViewProxy.instance().getIdentifierView().getPluginIcons().remove(component);
  234. // }
  235. // }
  236. mpv5.YabsViewProxy.instance().getIdentifierFrame().validate();
  237. mpv5.YabsViewProxy.instance().getIdentifierFrame().repaint();
  238. mP5Plugin = null;
  239. System.gc();
  240. }
  241. /**
  242. * Loads the given plugin (by calling <code>plugin.load(this)<code/>). If the plugin is a visible plugin, adds it to the main tab pane.</br>
  243. * If it is a <code>Runnable<code/>, it will be started on an new thread.
  244. * @param gin
  245. */
  246. public void loadPlugin(Plugin gin) throws Exception, Throwable {
  247. MP5Plugin plo = new mpv5.pluginhandling.MPPLuginLoader().getPlugin(QueryHandler.instanceOf().clone(Context.getFiles()).retrieveFile(gin.__getFilename()));
  248. if (plo != null) {
  249. loadPlugin(plo);
  250. } else {
  251. Log.Debug(this, "Plugin not loaded: " + plo);
  252. }
  253. }
  254. private static final List<Long> loadedPlugs = new Vector<Long>();
  255. /**
  256. * Loads the given plugin (by calling <code>plugin.load(this)<code/>). If the plugin is a visible plugin, adds it to the main tab pane.</br>
  257. * If it is a <code>Runnable<code/>, it will be started on an new thread.
  258. * @param mP5Plugin
  259. * @throws Exception
  260. * @throws Throwable
  261. */
  262. public static void loadPlugin(final MP5Plugin mP5Plugin) throws Exception, Throwable {
  263. if (!loadedPlugs.contains(mP5Plugin.getUID()) && mP5Plugin.isEnabled()) {
  264. loadedPlugs.add(mP5Plugin.getUID());
  265. final JLabel plab = new JLabel();
  266. plab.setDisabledIcon(new MPIcon(MPPLuginLoader.getErrorImage()).getIcon(18));
  267. try {
  268. try {
  269. Log.Debug(MPPLuginLoader.class, "Plugin: \n"
  270. + "Name: " + mP5Plugin.getName() + "\n"
  271. + "Vendor: " + mP5Plugin.getVendor() + "\n"
  272. + "IsComponent: " + mP5Plugin.isComponent() + "\n"
  273. + "IsRunnable: " + mP5Plugin.isRunnable());
  274. mP5Plugin.load(new MPView(Main.getApplication()));
  275. } catch (ClassCastException e) {
  276. Log.Debug(MPPLuginLoader.class, e.getMessage());
  277. Log.Debug(MPPLuginLoader.class, "Incompatible plugin found, asking to load anyway..");
  278. if (!Popup.Y_N_dialog(Messages.PLUGIN_INCOMPATIBLE, e)) {
  279. loadedPlugs.remove(mP5Plugin.getUID());
  280. return;
  281. }
  282. }
  283. if (mP5Plugin instanceof DatabaseObjectModifier) {
  284. Log.Debug(MPPLuginLoader.class, "Register modifying plugins: " + mP5Plugin);
  285. registeredModifiers.add((DatabaseObjectModifier) mP5Plugin);
  286. }
  287. if (mP5Plugin.isComponent()) {
  288. mpv5.YabsViewProxy.instance().getIdentifierView().addTab((JComponent) mP5Plugin, mP5Plugin.getName());
  289. }
  290. if (mP5Plugin.isRunnable() && mP5Plugin.isLoaded()) {
  291. Thread t = new Thread((Runnable) mP5Plugin);
  292. t.start();
  293. }
  294. if (mP5Plugin.getIcon() != null) {
  295. plab.setIcon(new MPIcon(mP5Plugin.getIcon()).getIcon(18));
  296. } else {
  297. plab.setIcon(new MPIcon(MPPLuginLoader.getDefaultPluginImage()).getIcon(18));
  298. }
  299. plab.setToolTipText("<html><b>" + mP5Plugin.getName() + " " + Messages.LOADED + "</b><br/><font size=-3>[" + mP5Plugin.getUID() + "]</html>");
  300. plab.addMouseListener(new MouseAdapter() {
  301. @Override
  302. public void mouseReleased(MouseEvent e) {
  303. if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
  304. JLabel source = (JLabel) e.getSource();
  305. JPopupMenu m = new JPopupMenu();
  306. JMenuItem n = new JMenuItem(Messages.UNLOAD.getValue());
  307. n.addActionListener(new ActionListener() {
  308. @Override
  309. public void actionPerformed(ActionEvent e) {
  310. unLoadPlugin(mP5Plugin);
  311. mpv5.Main.getApplication().getMainView().getMenuBar().remove(plab);
  312. mpv5.Main.getApplication().getMainView().getMenuBar().validate();
  313. }
  314. });
  315. m.add(n);
  316. m.show(plab, e.getX(), e.getY());
  317. }
  318. }
  319. });
  320. try {
  321. Main.getApplication().getMainView().getMenuBar().add(plab);
  322. mpv5.Main.getApplication().getMainView().getMenuBar().validate();
  323. } catch (Exception e) {
  324. Log.Debug(e);
  325. }
  326. } catch (Throwable e) {
  327. Log.Debug(e);
  328. // plab.setEnabled(false);
  329. throw e;
  330. }
  331. } else {
  332. Popup.notice(Messages.NOT_POSSIBLE + "\n" + mP5Plugin + " is already loaded and doesn't allow multiple instances.");
  333. Log.Debug(MPPLuginLoader.class, "Plugin does not allow multiple instances: " + mP5Plugin);
  334. }
  335. }
  336. /**
  337. * Removes the modifier from the list
  338. * @param m
  339. */
  340. public static void removeModifier(DatabaseObjectModifier m) {
  341. registeredModifiers.remove(m);
  342. }
  343. }