PageRenderTime 233ms CodeModel.GetById 168ms app.highlight 50ms RepoModel.GetById 6ms app.codeStats 1ms

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