/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 Jive Software. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.sparkimpl.plugin.viewer; import java.awt.Color; import java.awt.Component; import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JViewport; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.protocol.Protocol; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import org.jivesoftware.resource.Default; import org.jivesoftware.resource.Res; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.spark.PluginManager; import org.jivesoftware.spark.SparkManager; import org.jivesoftware.spark.component.MessageDialog; import org.jivesoftware.spark.component.VerticalFlowLayout; import org.jivesoftware.spark.plugin.Plugin; import org.jivesoftware.spark.plugin.PublicPlugin; import org.jivesoftware.spark.util.GraphicUtils; import org.jivesoftware.spark.util.ModelUtil; import org.jivesoftware.spark.util.SwingWorker; import org.jivesoftware.spark.util.URLFileSystem; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.settings.JiveInfo; import org.jivesoftware.sparkimpl.settings.local.LocalPreferences; import org.jivesoftware.sparkimpl.settings.local.SettingsManager; import org.jivesoftware.sparkimpl.updater.EasySSLProtocolSocketFactory; /** * Class to handle the viewing of installed and downloadable Plugins */ public class PluginViewer extends JPanel implements Plugin { private static final long serialVersionUID = -4249017716988031394L; private JTabbedPane tabbedPane; private boolean loaded = false; private String retrieveListURL = Default.getString("PLUGIN_REPOSITORY"); private JProgressBar progressBar; private JPanel installedPanel; private JPanel availablePanel; private JPanel deactivatedPanel; private LocalPreferences _prefs; private List<String> _deactivatedPlugins; public PluginViewer() { _prefs = SettingsManager.getLocalPreferences(); _deactivatedPlugins = _prefs.getDeactivatedPlugins(); EventQueue.invokeLater(new Runnable() { public void run() { tabbedPane = new JTabbedPane(); installedPanel = new JPanel(); availablePanel = new JPanel(); deactivatedPanel = new JPanel(); setLayout(new GridBagLayout()); installedPanel.setLayout(new VerticalFlowLayout( VerticalFlowLayout.TOP, 0, 0, true, false)); installedPanel.setBackground(Color.white); availablePanel.setLayout(new VerticalFlowLayout( VerticalFlowLayout.TOP, 0, 0, true, false)); availablePanel.setBackground(Color.white); // Add TabbedPane add(tabbedPane, new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0)); // Add Tabs tabbedPane.addTab(Res.getString("tab.installed.plugins"), new JScrollPane(installedPanel)); if (!Default.getBoolean(Default.INSTALL_PLUGINS_DISABLED)) { tabbedPane.addTab(Res.getString("tab.available.plugins"), new JScrollPane(availablePanel)); } loadInstalledPlugins(); loadDeactivatedPlugins(); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent changeEvent) { if (tabbedPane.getSelectedComponent().equals( ((JViewport)availablePanel.getParent()).getParent())) { loadAvailablePlugins(); loaded = true; } } }); } }); } private void loadInstalledPlugins() { PluginManager pluginManager = PluginManager.getInstance(); List<PublicPlugin> plugins = pluginManager.getPublicPlugins(); for (Object plugin1 : plugins) { PublicPlugin plugin = (PublicPlugin) plugin1; final SparkPlugUI ui = new SparkPlugUI(plugin); ui.useLocalIcon(); installedPanel.add(ui); addSparkPlugUIListener(ui); } } /** * Initializes the Deactivated Plugins Tab */ private void loadDeactivatedPlugins() { deactivatedPanel.setLayout(new VerticalFlowLayout( VerticalFlowLayout.TOP, 0, 0, true, false)); if (!Default.getBoolean(Default.DEINSTALL_PLUGINS_DISABLED)) { tabbedPane.addTab(Res.getString("tab.deactivated.plugins"), new JScrollPane(deactivatedPanel)); } for (final String s : _deactivatedPlugins) { PublicPlugin plg = new PublicPlugin(); plg.setName(s); final SparkPlugUI ui = new SparkPlugUI(plg); ui.useLocalIcon(); deactivatedPanel.add(ui); addDeactivatedListener(ui); } } public void initialize() { // Add Plugins Menu JMenuBar menuBar = SparkManager.getMainWindow().getJMenuBar(); // Get last menu which is help JMenu sparkMenu = menuBar.getMenu(0); JMenuItem viewPluginsMenu = new JMenuItem(); Action viewAction = new AbstractAction() { private static final long serialVersionUID = 6518407602062984752L; public void actionPerformed(ActionEvent e) { invokeViewer(); } }; viewAction.putValue(Action.NAME, Res.getString("menuitem.plugins")); viewAction.putValue(Action.SMALL_ICON, SparkRes.getImageIcon(SparkRes.PLUGIN_IMAGE)); viewPluginsMenu.setAction(viewAction); sparkMenu.insert(viewPluginsMenu, 2); } private boolean uninstall(final PublicPlugin plugin) { int ok = JOptionPane.showConfirmDialog(installedPanel, Res.getString("message.prompt.plugin.uninstall", plugin.getName()), Res.getString("title.confirmation"), JOptionPane.YES_NO_OPTION); if (ok == JOptionPane.YES_OPTION) { // DOENST DELETE ANYMORE, Plugin will be added to a 'do-not-load-list' // Delete main jar. // File pluginDir = plugin.getPluginDir(); // File pluginJAR = new File(plugin.getPluginDir().getParentFile(), // pluginDir.getName() + ".jar"); // File mainpluginJar = new // File(Spark.getBinDirectory().getParent()+"/plugins/"+pluginJAR.getName()); // pluginJAR.delete(); // mainpluginJar.delete(); List<String> deact = _prefs.getDeactivatedPlugins(); deact.add(plugin.getName()); _prefs.setDeactivatedPlugins(deact); _deactivatedPlugins = deact; final SparkPlugUI ui = new SparkPlugUI(plugin); deactivatedPanel.add(ui); addDeactivatedListener(ui); JOptionPane.showMessageDialog(this, Res.getString("message.restart.spark.changes"), Res.getString("title.reminder"), JOptionPane.INFORMATION_MESSAGE); PluginManager.getInstance().removePublicPlugin(plugin); return true; } return false; } private void invokeViewer() { PluginViewer viewer = new PluginViewer(); MessageDialog.showComponent(Res.getString("title.plugins"), "", null, viewer, SparkManager.getMainWindow(), 600, 600, false); } public void shutdown() { } public boolean canShutDown() { return false; } private void loadAvailablePlugins() { availablePanel.removeAll(); availablePanel.invalidate(); availablePanel.validate(); availablePanel.repaint(); JLabel label = new JLabel(Res.getString("message.loading.please.wait")); availablePanel.add(label); SwingWorker worker = new SwingWorker() { Collection<PublicPlugin> pluginList = null; public Object construct() { // Prepare HTTP post final GetMethod post = new GetMethod(retrieveListURL); // Get HTTP client Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(), 443)); final HttpClient httpclient = new HttpClient(); if(Default.getBoolean("PLUGIN_REPOSITORY_USE_PROXY")) { String proxyHost = System.getProperty("http.proxyHost"); String proxyPort = System.getProperty("http.proxyPort"); if (ModelUtil.hasLength(proxyHost) && ModelUtil.hasLength(proxyPort)) { try { httpclient.getHostConfiguration().setProxy(proxyHost, Integer.parseInt(proxyPort)); } catch (NumberFormatException e) { Log.error(e); } } } // Execute request try { int result = httpclient.executeMethod(post); if (result != 200) { return null; } pluginList = getPluginList(post.getResponseBodyAsStream()); } catch (Exception ex) { // Nothing to do } return "ok"; } public void finished() { final PluginManager pluginManager = PluginManager.getInstance(); if (pluginList == null) { availablePanel.removeAll(); availablePanel.invalidate(); availablePanel.validate(); availablePanel.repaint(); JOptionPane.showMessageDialog(availablePanel, Res.getString("message.plugins.not.available"), Res.getString("title.error"), JOptionPane.ERROR_MESSAGE); return; } Iterator<PublicPlugin> plugs = pluginList.iterator(); availablePanel.removeAll(); while (plugs.hasNext()) { PublicPlugin plugin = plugs.next(); if (!pluginManager.isInstalled(plugin)) { SparkPlugUI ui = new SparkPlugUI(plugin); availablePanel.add(ui); addSparkPlugUIListener(ui); } } availablePanel.invalidate(); availablePanel.validate(); availablePanel.repaint(); } }; worker.start(); } private void downloadPlugin(final PublicPlugin plugin) { // Prepare HTTP post final GetMethod post = new GetMethod(plugin.getDownloadURL()); // Get HTTP client Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(), 443)); final HttpClient httpclient = new HttpClient(); String proxyHost = System.getProperty("http.proxyHost"); String proxyPort = System.getProperty("http.proxyPort"); if (Default.getBoolean("PLUGIN_REPOSITORY_USE_PROXY")) { if (ModelUtil.hasLength(proxyHost) && ModelUtil.hasLength(proxyPort)) { try { httpclient.getHostConfiguration().setProxy(proxyHost, Integer.parseInt(proxyPort)); } catch (NumberFormatException e) { Log.error(e); } } } // Execute request try { int result = httpclient.executeMethod(post); if (result != 200) { return; } long length = post.getResponseContentLength(); int contentLength = (int)length; progressBar = new JProgressBar(0, contentLength); final JFrame frame = new JFrame(Res.getString("message.downloading", plugin.getName())); frame.setIconImage(SparkRes.getImageIcon(SparkRes.SMALL_MESSAGE_IMAGE).getImage()); final Thread thread = new Thread(new Runnable() { public void run() { try { Thread.sleep(2000); InputStream stream = post.getResponseBodyAsStream(); URL url = new URL(plugin.getDownloadURL()); String name = URLFileSystem.getFileName(url); String directoryName = URLFileSystem.getName(url); File pluginDownload = new File(PluginManager.PLUGINS_DIRECTORY, name); FileOutputStream out = new FileOutputStream(pluginDownload); copy(stream, out); out.close(); frame.dispose(); // Remove SparkPlugUI // Clear all selections Component[] comps = availablePanel.getComponents(); for (Component comp : comps) { if (comp instanceof SparkPlugUI) { SparkPlugUI sparkPlug = (SparkPlugUI) comp; if (sparkPlug.getPlugin().getDownloadURL().equals(plugin.getDownloadURL())) { availablePanel.remove(sparkPlug); _deactivatedPlugins.remove(sparkPlug.getPlugin().getName()); _prefs.setDeactivatedPlugins(_deactivatedPlugins); PluginManager.getInstance().addPlugin(sparkPlug.getPlugin()); sparkPlug.showOperationButton(); installedPanel.add(sparkPlug); sparkPlug.getPlugin().setPluginDir(new File(PluginManager.PLUGINS_DIRECTORY, directoryName)); installedPanel.invalidate(); installedPanel.repaint(); availablePanel.invalidate(); availablePanel.invalidate(); availablePanel.validate(); availablePanel.repaint(); } } } } catch (Exception ex) { // Nothing to do } finally { // Release current connection to the connection pool once you are done post.releaseConnection(); } } }); frame.getContentPane().setLayout(new GridBagLayout()); frame.getContentPane().add(new JLabel(Res.getString("message.downloading.spark.plug")), new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0)); frame.getContentPane().add(progressBar, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0)); frame.pack(); frame.setSize(400, 100); GraphicUtils.centerWindowOnComponent(frame, this); frame.setVisible(true); thread.start(); } catch (IOException e) { Log.error(e); } } public Collection<PublicPlugin> getPluginList(InputStream response) { final List<PublicPlugin> pluginList = new ArrayList<PublicPlugin>(); SAXReader saxReader = new SAXReader(); Document pluginXML = null; try { pluginXML = saxReader.read(response); } catch (DocumentException e) { Log.error(e); } List<? extends Node> plugins = pluginXML.selectNodes("/plugins/plugin"); for (Node plugin1 : plugins) { PublicPlugin publicPlugin = new PublicPlugin(); String clazz; String name = null; try { Element plugin = (Element) plugin1; try { String version = plugin.selectSingleNode("minSparkVersion").getText(); if (!isGreaterOrEqual(JiveInfo.getVersion(), version)) { Log.error("Unable to load plugin " + name + " due to min version incompatibility."); continue; } } catch (Exception e) { Log.error("Unable to load plugin " + name + " due to no minSparkVersion."); continue; } name = plugin.selectSingleNode("name").getText(); clazz = plugin.selectSingleNode("class").getText(); publicPlugin.setPluginClass(clazz); publicPlugin.setName(name); try { String version = plugin.selectSingleNode("version").getText(); publicPlugin.setVersion(version); String author = plugin.selectSingleNode("author").getText(); publicPlugin.setAuthor(author); Node emailNode = plugin.selectSingleNode("email"); if (emailNode != null) { publicPlugin.setEmail(emailNode.getText()); } Node descriptionNode = plugin.selectSingleNode("description"); if (descriptionNode != null) { publicPlugin.setDescription(descriptionNode.getText()); } Node homePageNode = plugin.selectSingleNode("homePage"); if (homePageNode != null) { publicPlugin.setHomePage(homePageNode.getText()); } Node downloadNode = plugin.selectSingleNode("downloadURL"); if (downloadNode != null) { String downloadURL = downloadNode.getText(); publicPlugin.setDownloadURL(downloadURL); } Node changeLogNode = plugin.selectSingleNode("changeLog"); if (changeLogNode != null) { publicPlugin.setChangeLogURL(changeLogNode.getText()); } Node readMeNode = plugin.selectSingleNode("readme"); if (readMeNode != null) { publicPlugin.setReadMeURL(readMeNode.getText()); } Node smallIcon = plugin.selectSingleNode("smallIcon"); if (smallIcon != null) { publicPlugin.setSmallIconAvailable(true); } Node largeIcon = plugin.selectSingleNode("largeIcon"); if (largeIcon != null) { publicPlugin.setLargeIconAvailable(true); } } catch (Exception e) { Log.error("Error retrieving PluginInformation from xml.",e); } pluginList.add(publicPlugin); } catch (Exception ex) { ex.printStackTrace(); } } return pluginList; } /** * Common code for copy routines. By convention, the streams are * closed in the same method in which they were opened. Thus, * this method does not close the streams when the copying is done. * * @param in Stream to copy from. * @param out Stream to copy to. */ private void copy(final InputStream in, final OutputStream out) { int read = 0; while (true) { try { try { Thread.sleep(10); } catch (InterruptedException e) { Log.error(e); } final byte[] buffer = new byte[4096]; int bytesRead = in.read(buffer); if (bytesRead < 0) { break; } out.write(buffer, 0, bytesRead); read += bytesRead; final int readprogr = read; EventQueue.invokeLater(new Runnable() { @Override public void run() { progressBar.setValue(readprogr); } }); } catch (IOException e) { Log.error(e); } } } private void addSparkPlugUIListener(final SparkPlugUI ui) { ui.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent mouseEvent) { // Clear all selections Component[] comps = installedPanel.getComponents(); for (Component comp : comps) { if (comp instanceof SparkPlugUI) { SparkPlugUI sparkPlug = (SparkPlugUI) comp; sparkPlug.setSelected(false); } } // Clear all selections comps = availablePanel.getComponents(); for (Component comp : comps) { if (comp instanceof SparkPlugUI) { SparkPlugUI sparkPlug = (SparkPlugUI) comp; sparkPlug.setSelected(false); } } ui.setSelected(true); final PluginManager pluginManager = PluginManager.getInstance(); ui.getInstallButton().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { boolean isInstalled = pluginManager.isInstalled(ui.getPlugin()); if (isInstalled) { boolean uninstalled = uninstall(ui.getPlugin()); if (uninstalled) { installedPanel.remove(ui); installedPanel.invalidate(); installedPanel.repaint(); installedPanel.revalidate(); } } else { downloadPlugin(ui.getPlugin()); } } }); } }); } /** * Adds the MouseClick Listener to the PluginPreview <br> * Adds the MouseClick Listener to the InstallButton * @param ui */ private void addDeactivatedListener(final SparkPlugUI ui) { ui.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { for(Component c : deactivatedPanel.getComponents()) { if (c instanceof SparkPlugUI) { ((SparkPlugUI)c).setSelected(false); } } ui.setSelected(true); } }); ui.getInstallButton().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { deactivatedPanel.remove(ui); _deactivatedPlugins.remove(ui.getPlugin().getName()); _prefs.setDeactivatedPlugins(_deactivatedPlugins); deactivatedPanel.repaint(); deactivatedPanel.revalidate(); } }); } public void uninstall() { // Do nothing. } /** * Returns true if the first version number is greater than the second. * * @param firstVersion the first version number. * @param secondVersion the second version number. * @return returns true if the first version is greater than the second. */ public boolean isGreaterOrEqual(String firstVersion, String secondVersion) { return firstVersion.compareTo(secondVersion) >= 0; } public boolean isLoaded() { return loaded; } }