/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapid_i.deployment.update.client; import java.awt.Desktop; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URISyntaxException; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractButton; import javax.swing.AbstractListModel; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToggleButton; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import com.rapidminer.RapidMiner; import com.rapidminer.deployment.client.wsimport.AccountService; import com.rapidminer.deployment.client.wsimport.PackageDescriptor; import com.rapidminer.deployment.client.wsimport.UpdateService; import com.rapidminer.gui.tools.ExtendedHTMLJEditorPane; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.dialogs.ButtonDialog; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Tools; import com.rapidminer.tools.plugin.Dependency; /** * * @author Simon Fischer * */ public class UpdateListPanel extends JPanel { private final class PackageListModel extends AbstractListModel { private static final long serialVersionUID = 1L; @Override public Object getElementAt(int index) { return descriptors.get(index); } @Override public int getSize() { return descriptors.size(); } private void update(PackageDescriptor descr) { int index = descriptors.indexOf(descr); fireContentsChanged(this, index, index); } private void add(PackageDescriptor desc) { descriptors.add(desc); fireIntervalAdded(this, descriptors.size()-1, descriptors.size()-1); } } private final Map<PackageDescriptor, Boolean> selectionMap = new HashMap<PackageDescriptor, Boolean>(); private final Map<PackageDescriptor, List<Dependency>> dependencyMap = new HashMap<PackageDescriptor, List<Dependency>>(); /** Read the comment of {@link #isPurchased(PackageDescriptor)}. */ private final Set<String> purchasedPackages = new HashSet<String>(); private final UpdateDialog updateDialog; private static final long serialVersionUID = 1L; private final ExtendedHTMLJEditorPane displayPane = new ExtendedHTMLJEditorPane("text/html", "<html></html>"); private final JList updateList; private final JToggleButton installButton = new JToggleButton(new ResourceAction("update.select") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent arg0) { toggleSelection(); } }); private final JButton fetchFromAccountButton = new JButton(new ResourceAction("update.fetch_bookmarks") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { fetchBookmarks(); } }); private final PackageListModel listModel = new PackageListModel(); private final List<PackageDescriptor> descriptors; private final JLabel sizeLabel = new JLabel(); public UpdateListPanel(UpdateDialog dialog, List<PackageDescriptor> descriptors, String[] preselectedExtensions) { for (String pE : preselectedExtensions) { for (PackageDescriptor desc : descriptors) { if (desc.getPackageId().equals(pE)) { selectionMap.put(desc, true); } } } for (PackageDescriptor desc : descriptors) { if (desc.getDependencies() != null) { List<Dependency> dep = Dependency.parse(desc.getDependencies()); if (!dep.isEmpty()) { dependencyMap.put(desc, dep); } } } this.updateDialog = dialog; displayPane.installDefaultStylesheet(); displayPane.setEditable(false); this.descriptors = descriptors; updateList = new JList(listModel); updateList.setCellRenderer(new UpdateListCellRenderer(this)); updateList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { PackageDescriptor desc = (PackageDescriptor) updateList.getSelectedValue(); if (desc != null) { displayPane.setText(UpdateListPanel.this.toString(desc)); installButton.setSelected(isSelected(desc)); } } } }); updateList.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { toggleSelection(); } } }); displayPane.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) { try { Desktop.getDesktop().browse(e.getURL().toURI()); } catch (Exception e1) { SwingTools.showVerySimpleErrorMessage("cannot_open_browser"); } } } }); updateSize(); setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.FIRST_LINE_START; c.fill = GridBagConstraints.BOTH; c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 1; c.weighty = 0.3; c.insets = new Insets(0, 0, ButtonDialog.GAP, 0); JScrollPane updateListPane = new ExtendedJScrollPane(updateList); updateListPane.setPreferredSize(new Dimension(400, 300)); updateListPane.setBorder(ButtonDialog.createTitledBorder("Available Updates")); add(updateListPane, c); c.weighty = 0.7; JScrollPane jScrollPane = new ExtendedJScrollPane(displayPane); jScrollPane.setPreferredSize(new Dimension(400, 300)); jScrollPane.setBorder(ButtonDialog.createTitledBorder("Description")); add(jScrollPane, c); c.weighty = 0; add(sizeLabel, c); } public AbstractButton getInstallButton() { return installButton; } public AbstractButton getFetchFromAccountButton() { return fetchFromAccountButton; } private String toString(PackageDescriptor descriptor) { StringBuilder b = new StringBuilder("<html>"); b.append("<h2>").append(descriptor.getName() + "</h2>"); Date date = new Date(descriptor.getCreationTime().toGregorianCalendar().getTimeInMillis()); b.append("<hr noshade=\"true\"/><strong>").append(descriptor.getVersion()).append(", released ").append(Tools.formatDate(date)); b.append(", ").append(Tools.formatBytes(descriptor.getSize())).append("</strong>"); if ((descriptor.getDependencies() != null) && !descriptor.getDependencies().isEmpty()) { b.append("<br/>Depends on: " + descriptor.getDependencies()); } b.append("<p>").append(descriptor.getDescription()).append("</p>"); // Before you are shocked, read the comment of isPurchased() :-) if (UpdateManager.COMMERCIAL_LICENSE_NAME.equals(descriptor.getLicenseName())) { if (isPurchased(descriptor)) { b.append("<p>You have purchased this package. However, you cannot install this extension with this version of RapidMiner. Please upgrade first.</p>"); } else { try { b.append("<p><a href=" + UpdateManager.getUpdateServerURI("/shop/" + descriptor.getPackageId()).toString() + ">Order this extension.</a></p><p>You cannot install this extension with this pre-release of RapidMiner. Please upgrade first.</p>"); } catch (URISyntaxException e) { } } } b.append("<br/><p><a href=\""+UpdateManager.getBaseUrl()+"/faces/product_details.xhtml?productId="+descriptor.getPackageId()+"\">Extension homepage</a></p>"); b.append("</html>"); return b.toString(); } public boolean isSelected(PackageDescriptor desc) { Boolean selected = selectionMap.get(desc); return (selected != null) && selected.booleanValue(); } private void updateSize() { int totalSize = getTotalSize(); if (totalSize > 0) { sizeLabel.setText("Total download size: " + Tools.formatBytes(totalSize) + " (This may be less, if incremental updates are possible.)"); } else { sizeLabel.setText(" "); } } private int getTotalSize() { int totalSize = 0; for (Map.Entry<PackageDescriptor, Boolean> entry : selectionMap.entrySet()) { if (entry.getValue()) { totalSize += entry.getKey().getSize(); } } return totalSize; } public void startUpdate() { final List<PackageDescriptor> downloadList = new LinkedList<PackageDescriptor>(); for (Entry<PackageDescriptor, Boolean> entry : selectionMap.entrySet()) { if (entry.getValue()) { downloadList.add(entry.getKey()); } } updateDialog.startUpdate(downloadList); } private boolean isUpToDate(PackageDescriptor desc) { ManagedExtension ext = ManagedExtension.get(desc.getPackageId()); if (ext != null) { String remoteVersion = ManagedExtension.normalizeVersion(desc.getVersion()); String myVersion = ManagedExtension.normalizeVersion(ext.getLatestInstalledVersion()); if ((myVersion != null) && (remoteVersion.compareTo(myVersion) <= 0)) { return true; } else { return false; } } else { return false; } } private void toggleSelection() { PackageDescriptor desc = (PackageDescriptor) updateList.getSelectedValue(); if (desc != null) { boolean select = !isSelected(desc); if (isUpToDate(desc)) { select = false; } if (desc.getPackageTypeName().equals("RAPIDMINER_PLUGIN")) { if (select) { resolveDependencies(desc); } } else if (desc.getPackageTypeName().equals("STAND_ALONE")) { String longVersion = RapidMiner.getLongVersion(); String myVersion = ManagedExtension.normalizeVersion(longVersion); String remoteVersion = ManagedExtension.normalizeVersion(desc.getVersion()); if ((myVersion != null) && (remoteVersion.compareTo(myVersion) <= 0)) { select = false; } } if (UpdateManager.COMMERCIAL_LICENSE_NAME.equals(desc.getLicenseName()) && !isPurchased(desc)) { select = false; SwingTools.showMessageDialog("purchase_package", desc.getName()); } selectionMap.put(desc, select); listModel.update(desc); } updateSize(); } private void resolveDependencies(PackageDescriptor desc) { List<Dependency> deps = dependencyMap.get(desc); if (deps != null) { for (Dependency dep : deps) { for (PackageDescriptor other : descriptors) { if (other.getPackageId().equals(dep.getPluginExtensionId())) { Boolean selected = selectionMap.get(other); boolean selectedB = (selected != null) && selected.booleanValue(); if (!selectedB && !isUpToDate(other)) { selectionMap.put(other, true); resolveDependencies(other); } break; } } } } } /** * Currently, this is an unused feature. There are no extensions that can be purchased. Don't be afraid, RapidMiner * is, and will always be, open source and free. However, future extensions like connectors to SAP or other data * sources requiring proprietary drivers with expensive license fees may only be available on a commercial basis, * for obvious reasons :-) */ public boolean isPurchased(PackageDescriptor desc) { return purchasedPackages.contains(desc.getPackageId()); } /** * Connects to rapidupdate.de to fetch the bookmarks and automatically select them. */ public void fetchBookmarks() { // TODO: Do in progress thread List<String> bookmarks; try { AccountService accountService = UpdateManager.getAccountService(); bookmarks = accountService.getBookmarkedProducts("rapidminer"); } catch (Exception e) { SwingTools.showSimpleErrorMessage("error_accessing_marketplace_account", e, e.toString()); return; } Map<String,PackageDescriptor> packDescById = new HashMap<String,PackageDescriptor>(); for (PackageDescriptor desc : descriptors) { packDescById.put(desc.getPackageId(), desc); } //for (PackageDescriptor desc : descriptors) { for (String bookmarkedId : bookmarks) { PackageDescriptor desc = packDescById.get(bookmarkedId); //if (bookmarks.contains(desc.getPackageId())) { LogService.getRoot().log(Level.INFO, "Looking up "+bookmarkedId); if (desc == null) { LogService.getRoot().log(Level.INFO, "Bookmarked package "+bookmarkedId+" was unlisted. Fetching now."); String rmPlatform = "ANY"; //Launcher.getPlatform(); try { UpdateService updateService = UpdateManager.getService(); String latestRMVersion = updateService.getLatestVersion(bookmarkedId, rmPlatform); desc = updateService.getPackageInfo(bookmarkedId, latestRMVersion, rmPlatform); if (desc != null) { packDescById.put(desc.getPackageId(), desc); listModel.add(desc); } } catch (Exception e) { SwingTools.showSimpleErrorMessage("error_during_update", e); return; } } if (desc != null) { ManagedExtension ext = ManagedExtension.get(desc.getPackageId()); final boolean upToDate; if (ext == null) { upToDate = false; } else { String installed = ext.getLatestInstalledVersion(); if (installed != null) { upToDate = installed.compareTo(desc.getVersion()) >= 0; } else { upToDate = false; } } if (!upToDate) { selectionMap.put(desc, true); resolveDependencies(desc); } continue; } } updateList.repaint(); } // public static AccountService getAccountService() { // AccountServiceService ass = new AccountServiceService(); // AccountService accountService = ass.getAccountServicePort(); // return accountService; // } }