001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.plugin;
020
021import icy.main.Icy;
022import icy.plugin.abstract_.Plugin;
023import icy.plugin.interface_.PluginImageAnalysis;
024import icy.plugin.interface_.PluginNoEDTConstructor;
025import icy.plugin.interface_.PluginStartAsThread;
026import icy.plugin.interface_.PluginThreaded;
027import icy.system.IcyExceptionHandler;
028import icy.system.audit.Audit;
029import icy.system.thread.ThreadUtil;
030import icy.util.ClassUtil;
031
032import java.util.concurrent.Callable;
033
034/**
035 * This class launch plugins and register them to the main application.<br>
036 * The launch can be in a decicated thread or in the EDT.
037 * 
038 * @author Fabrice de Chaumont & Stephane
039 */
040public class PluginLauncher
041{
042    protected static class PluginExecutor implements Callable<Boolean>, Runnable
043    {
044        final Plugin plugin;
045
046        public PluginExecutor(Plugin plugin)
047        {
048            super();
049
050            this.plugin = plugin;
051        }
052
053        @Override
054        public Boolean call() throws Exception
055        {
056            // some plugins (as EzPlug) do not respect the PluginActionable convention (run() method
057            // contains all the process)
058            // so we can't yet use this bloc of code
059
060            // if (plugin instanceof PluginActionable)
061            // ((PluginActionable) plugin).run();
062            // // keep backward compatibility
063            // else if (plugin instanceof PluginImageAnalysis)
064            // ((PluginImageAnalysis) plugin).compute();
065
066            // keep backward compatibility
067            if (plugin instanceof PluginImageAnalysis)
068                ((PluginImageAnalysis) plugin).compute();
069
070            return Boolean.TRUE;
071        }
072
073        @Override
074        public void run()
075        {
076            try
077            {
078                call();
079            }
080            catch (Throwable t)
081            {
082                IcyExceptionHandler.handleException(plugin.getDescriptor(), t, true);
083            }
084        }
085    }
086
087    /**
088     * Executes the specified plugin.<br>
089     * If the specified plugin implements {@link PluginThreaded} then the plugin will be executed in
090     * a separate thread and the method will return before completion.<br>
091     * In other case the plugin is executed on the EDT by using {@link ThreadUtil#invokeNow(Callable)} and so method
092     * return after completion.
093     * 
094     * @throws InterruptedException
095     *         if the current thread was interrupted while waiting for execution on EDT.
096     * @throws Exception
097     *         if the computation threw an exception (only when plugin is executed on EDT).
098     */
099    private static void internalExecute(final Plugin plugin) throws Exception
100    {
101        if (plugin instanceof PluginThreaded)
102        {
103            // headless mode --> command line direct execution
104            if (Icy.getMainInterface().isHeadLess())
105                ((PluginThreaded) plugin).run();
106            else
107                new Thread((PluginThreaded) plugin, plugin.getName()).start();
108        }
109        else
110        {
111            final PluginExecutor executor = new PluginExecutor(plugin);
112
113            // headless mode --> command line direct execution
114            if (Icy.getMainInterface().isHeadLess())
115                executor.call();
116            // keep backward compatibility
117            else if (plugin instanceof PluginStartAsThread)
118                new Thread(executor, plugin.getName()).start();
119            // direct launch in EDT now (no thread creation)
120            else
121                ThreadUtil.invokeNow((Callable<Boolean>) executor);
122        }
123    }
124
125    /**
126     * Creates a new instance of the specified plugin and returns it.<br>
127     * 
128     * @param plugin
129     *        descriptor of the plugin we want to create an instance for
130     * @param register
131     *        if we want to register the plugin in the active plugin list
132     * @see #startSafe(PluginDescriptor)
133     */
134    public static Plugin create(final PluginDescriptor plugin, boolean register) throws Exception
135    {
136        final Class<? extends Plugin> clazz = plugin.getPluginClass();
137        final Plugin result;
138
139        // use the special PluginNoEDTConstructor interface or headless mode ?
140        if (ClassUtil.isSubClass(clazz, PluginNoEDTConstructor.class) || Icy.getMainInterface().isHeadLess())
141            result = clazz.newInstance();
142        else
143        {
144            // create the plugin instance on the EDT
145            result = ThreadUtil.invokeNow(new Callable<Plugin>()
146            {
147                @Override
148                public Plugin call() throws Exception
149                {
150                    return clazz.newInstance();
151                }
152            });
153        }
154
155        // register plugin
156        if (register)
157            Icy.getMainInterface().registerPlugin(result);
158
159        return result;
160    }
161
162    /**
163     * Creates a new instance of the specified plugin and returns it.<br>
164     * The plugin is automatically registered to the list of active plugins.
165     * 
166     * @param plugin
167     *        descriptor of the plugin we want to create an instance for
168     * @see #startSafe(PluginDescriptor)
169     */
170    public static Plugin create(final PluginDescriptor plugin) throws Exception
171    {
172        return create(plugin, true);
173    }
174
175    /**
176     * Starts the specified plugin (catched exception version).<br>
177     * Returns the plugin instance (only meaningful for {@link PluginThreaded} plugin) or <code>null</code> if an error
178     * occurred.
179     * 
180     * @param plugin
181     *        descriptor of the plugin we want to start
182     * @see #startSafe(PluginDescriptor)
183     */
184    public static Plugin start(PluginDescriptor plugin)
185    {
186        final Plugin result;
187
188        try
189        {
190            try
191            {
192                // create plugin instance
193                result = create(plugin);
194            }
195            catch (IllegalAccessException e)
196            {
197                System.err.println("Cannot start plugin " + plugin.getName() + " :");
198                System.err.println(e.getMessage());
199                return null;
200            }
201            catch (InstantiationException e)
202            {
203                System.err.println("Cannot start plugin " + plugin.getName() + " :");
204                System.err.println(e.getMessage());
205                return null;
206            }
207
208            // audit
209            Audit.pluginLaunched(result);
210            // execute plugin
211            if (result instanceof PluginImageAnalysis)
212                internalExecute(result);
213
214            return result;
215        }
216        catch (InterruptedException e)
217        {
218            // we just ignore interruption
219        }
220        catch (Throwable t)
221        {
222            IcyExceptionHandler.handleException(plugin, t, true);
223        }
224
225        return null;
226    }
227
228    /**
229     * @deprecated Use {@link #start(PluginDescriptor)} instead.<br>
230     *             You can retrieve a {@link PluginDescriptor} from the class name by using
231     *             {@link PluginLoader#getPlugin(String)} method.
232     */
233    @Deprecated
234    public static Plugin start(String pluginClassName)
235    {
236        final PluginDescriptor plugin = PluginLoader.getPlugin(pluginClassName);
237
238        if (plugin != null)
239            return start(plugin);
240
241        return null;
242    }
243
244    /**
245     * Same as {@link #start(PluginDescriptor)} except it throws {@link Exception} on error
246     * so user can handle them.
247     * 
248     * @param plugin
249     *        descriptor of the plugin we want to start
250     *        compatibility)
251     * @throws InterruptedException
252     *         if the current thread was interrupted while waiting for execution on EDT.
253     * @throws Exception
254     *         if the computation threw an exception (only when plugin is executed on EDT).
255     */
256    public static Plugin startSafe(PluginDescriptor plugin) throws Exception
257    {
258        final Plugin result;
259
260        // create plugin instance
261        result = create(plugin);
262
263        // audit
264        Audit.pluginLaunched(result);
265        // execute plugin
266        if (result instanceof PluginImageAnalysis)
267            internalExecute(result);
268
269        return result;
270    }
271
272    /**
273     * @deprecated Use {@link #startSafe(PluginDescriptor)} instead.<br>
274     *             You can retrieve a {@link PluginDescriptor} from the class name by using
275     *             {@link PluginLoader#getPlugin(String)} method.
276     */
277    @Deprecated
278    public static Plugin startSafe(String pluginClassName) throws Exception
279    {
280        final PluginDescriptor plugin = PluginLoader.getPlugin(pluginClassName);
281
282        if (plugin != null)
283            return startSafe(plugin);
284
285        return null;
286    }
287
288    /**
289     * @deprecated Use {@link #start(PluginDescriptor)} instead.
290     */
291    @Deprecated
292    public synchronized static void launch(PluginDescriptor descriptor)
293    {
294        start(descriptor);
295    }
296}