You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ma...@apache.org on 2020/02/27 17:46:12 UTC

[netbeans] branch master updated: [NETBEANS-2183] Workaround change in CDNJS search API

This is an automated email from the ASF dual-hosted git repository.

matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new b89f344  [NETBEANS-2183] Workaround change in CDNJS search API
b89f344 is described below

commit b89f344b6b2178026fd24132fcd5c96b86f9e621
Author: Matthias Bläsing <mb...@doppel-helix.eu>
AuthorDate: Thu Feb 27 18:45:55 2020 +0100

    [NETBEANS-2183] Workaround change in CDNJS search API
    
    The seach API of CDNJS returned the available version in the past. This
    is no longer the case and the versions need to be fetch independently.
---
 .../javascript/cdnjs/LibraryCustomizer.java        |   6 -
 .../modules/javascript/cdnjs/LibraryProvider.java  | 472 +++++++++++----------
 .../modules/javascript/cdnjs/ui/SearchPanel.java   |  69 +--
 .../javascript/cdnjs/ui/SelectionPanel.java        |  55 +--
 .../javascript/cdnjs/LibraryProviderTest.java      |  46 +-
 5 files changed, 304 insertions(+), 344 deletions(-)

diff --git a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryCustomizer.java b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryCustomizer.java
index a11448c..6a00529 100644
--- a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryCustomizer.java
+++ b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryCustomizer.java
@@ -83,12 +83,6 @@ public class LibraryCustomizer implements ProjectCustomizer.CompositeCategoryPro
         String libraryFolder = LibraryUtils.getLibraryFolder(project);
         final SelectionPanel customizer = new SelectionPanel(project, libraries, webRoot, libraryFolder);
         category.setStoreListener(new StoreListener(project, webRoot, customizer));
-        category.setCloseListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                customizer.dispose();
-            }
-        });
         return customizer;
     }
 
diff --git a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryProvider.java b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryProvider.java
index 7b9d2e5..a3b75f5 100644
--- a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryProvider.java
+++ b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/LibraryProvider.java
@@ -18,8 +18,6 @@
  */
 package org.netbeans.modules.javascript.cdnjs;
 
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -39,6 +37,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.json.simple.JSONArray;
@@ -47,7 +46,6 @@ import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.Pair;
-import org.openide.util.RequestProcessor;
 
 /**
  * CDNJS library provider, i.e., provider of the libraries available
@@ -68,15 +66,30 @@ import org.openide.util.RequestProcessor;
  * @author Jan Stola
  */
 public final class LibraryProvider {
+    /** Name of the 'versions' property. */
+    private static final String PROPERTY_VERSIONS = "assets"; // NOI18N
+    /** Name of the 'version name' property. */
+    private static final String PROPERTY_VERSION_NAME = "version"; // NOI18N
+    /** Name of the 'files' property. */
+    private static final String PROPERTY_FILES = "files"; // NOI18N
+    /** Name of the 'file name' property. */
+    private static final String PROPERTY_FILE_NAME = "name"; // NOI18N
+    /** Name of the 'result' property. */
+    private static final String PROPERTY_RESULT = "results"; // NOI18N
+    /** Name of the 'name' property. */
+    private static final String PROPERTY_NAME = "name"; // NOI18N
+    /** Name of the 'description' property. */
+    private static final String PROPERTY_DESCRIPTION = "description"; // NOI18N
+    /** Name of the 'homepage' property. */
+    private static final String PROPERTY_HOMEPAGE = "homepage"; // NOI18N
+
     /** The only instance of this provider. */
     private static final LibraryProvider INSTANCE = new LibraryProvider();
-    /** Request processor used by this class. */
-    private static final RequestProcessor RP = new RequestProcessor(LibraryProvider.class.getName(), 3);
     /** Cache of the search results. It maps the search term to the search result. */
     private final Map<String,WeakReference<Library[]>> cache =
-            Collections.synchronizedMap(new HashMap<String,WeakReference<Library[]>>());
-    /** Property change support. */
-    private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
+            Collections.synchronizedMap(new HashMap<>());
+    private final Map<String,WeakReference<Library>> entryCache =
+            Collections.synchronizedMap(new HashMap<>());
 
     /**
      * Creates a new {@code LibraryProvider}.
@@ -94,66 +107,109 @@ public final class LibraryProvider {
     }
 
     /**
-     * Adds a property change listener to this provider. The listener
-     * is notified whenever a new search result is available.
-     * 
-     * @param listener listener to register.
-     */
-    public void addPropertyChangeListener(PropertyChangeListener listener) {
-        propertyChangeSupport.addPropertyChangeListener(listener);
-    }
-
-    /**
-     * Removes a property change listener from this provider.
-     * 
-     * @param listener listener to unregister.
-     */
-    public void removePropertyChangeListener(PropertyChangeListener listener) {
-        propertyChangeSupport.removePropertyChangeListener(listener);
-    }
-
-    /**
-     * Finds the libraries matching the given search term. This method returns
-     * {@code null} when the result of the search is not present in the cache
-     * already. It starts the corresponding search in this case and reports
-     * its result by firing a property change event with the property name
-     * equal to the given search term. The result of the search can be obtained
-     * through the new value property of the event or by another invocation
-     * of this method. The first approach is recommended as it allows
-     * to recognize that the search failed. The new value property of the
-     * event is set to {@code null} in such case.
-     * 
+     * Finds the libraries matching the given search term. The resulting
+     * {@code Library} instances potentially don't have the versions property
+     * set. If that is the case the Library needs to be updated with the
+     * {@link #updateLibraryVersions} call.
+     *
      * @param searchTerm search term.
-     * @param priority priority from {@link Thread#MIN_PRIORITY} to {@link Thread#MAX_PRIORITY}.
-     * @return libraries matching the given search term when the search result
-     * is already available (which usually doesn't happen). Returns {@code null}
      * otherwise.
      */
-    public Library[] findLibraries(String searchTerm, int priority) {
+    public Library[] findLibraries(String searchTerm) {
         WeakReference<Library[]> reference = cache.get(searchTerm);
         Library[] result = null;
         if (reference != null) {
             result = reference.get();
         }
         if (result == null) {
-            SearchTask task = new SearchTask(searchTerm);
-            RP.post(task, 0, priority);
+            String searchURL = getSearchURL(searchTerm);
+            String urlContent = readUrl(searchURL);
+            Library[] libraries = null;
+            if (urlContent != null) {
+                libraries = parse(urlContent);
+            }
+            cache.put(searchTerm, new WeakReference<>(libraries));
+            result = libraries;
         }
         return result;
     }
 
     /**
-     * Updates the cache with the result of the search.
-     * 
-     * @param searchTerm search term.
-     * @param libraries libraries matching the search term.
+     * Update a library returned by {@link #findLibraries(java.lang.String)}.
+     * The full library data is fetched and the {@code versions} property is
+     * filled.
+     *
+     * @param library to be updated
+     */
+    public void updateLibraryVersions(Library library) {
+        Objects.nonNull(library);
+        if(library.getVersions() != null && library.getVersions().length > 0) {
+            return;
+        }
+        Library cachedLibrary = getCachedLibrary(library.getName());
+        if(cachedLibrary != null) {
+            library.setVersions(cachedLibrary.getVersions());
+            return;
+        }
+        String data = readUrl(getLibraryDataUrl(library.getName()));
+        if(data != null) {
+            try {
+                JSONParser parser = new JSONParser();
+                JSONObject libraryData = (JSONObject)parser.parse(data);
+                updateLibrary(library, libraryData);
+                entryCache.put(library.getName(), new WeakReference<>(library));
+            } catch (ParseException ex) {
+                Logger.getLogger(LibraryProvider.class.getName()).log(Level.INFO, null, ex);
+            }
+        }
+    }
+
+    /**
+     * Load the full data for the supplied library. All fields are populated,
+     * including the {@code versions} property.
+     *
+     * @param libraryName
+     * @return
      */
-    void updateCache(String searchTerm, Library[] libraries) {
-        if (libraries != null) {
-            WeakReference<Library[]> reference = new WeakReference<>(libraries);
-            cache.put(searchTerm, reference);
+    public Library loadLibrary(String libraryName) {
+        Library cachedLibrary = getCachedLibrary(libraryName);
+        if(cachedLibrary != null) {
+            return cachedLibrary;
+        }
+        String data = readUrl(getLibraryDataUrl(libraryName));
+        if (data != null) {
+            try {
+                JSONParser parser = new JSONParser();
+                JSONObject libraryData = (JSONObject) parser.parse(data);
+                Library library = createLibrary(libraryData);
+                entryCache.put(library.getName(), new WeakReference<>(library));
+                return library;
+            } catch (ParseException ex) {
+                Logger.getLogger(LibraryProvider.class.getName()).log(Level.INFO, null, ex);
+            }
+        }
+        return null;
+    }
+
+    private Library getCachedLibrary(String name) {
+        WeakReference<Library> cachedEntry = entryCache.get(name);
+        if (cachedEntry != null) {
+            return cachedEntry.get();
+        } else {
+            return null;
         }
-        propertyChangeSupport.firePropertyChange(searchTerm, null, libraries);
+    }
+
+    private String getLibraryDataUrl(String libraryName) {
+        String encodedLibraryName;
+        try {
+            encodedLibraryName = URLEncoder.encode(libraryName, "UTF-8"); // NOI18N
+        } catch (UnsupportedEncodingException ueex) {
+            // Should not happen, UTF-8 should be supported everywhere
+            Logger.getLogger(LibraryProvider.class.getName()).log(Level.SEVERE, null, ueex);
+            encodedLibraryName = libraryName;
+        }
+        return String.format(ASSET_URL_PATTERN, encodedLibraryName);
     }
 
     /**
@@ -203,6 +259,10 @@ public final class LibraryProvider {
     static final String SEARCH_URL_PREFIX =
             System.getProperty("netbeans.cdnjs.searchurl", // NOI18N
             "https://api.cdnjs.com/libraries?fields=description,homepage,assets&search="); // NOI18N
+    /** URL to fetch asset data */
+    static final String ASSET_URL_PATTERN =
+            System.getProperty("netbeans.cdnjs.asseturlpattern", // NOI18N
+            "https://api.cdnjs.com/libraries/%1$s?fields=name,description,homepage,assets"); // NOI18N
 
     /**
      * Comparator that helps to sort library versions.
@@ -214,199 +274,161 @@ public final class LibraryProvider {
         }
     };
 
+    private static void extractVersionInformation(JSONObject data, Library library) {
+        JSONArray versionsData = (JSONArray) data.get(PROPERTY_VERSIONS);
+        if (versionsData != null) {
+            Library.Version[] versions = new Library.Version[versionsData.size()];
+            for (int i = 0; i < versions.length; i++) {
+                JSONObject versionData = (JSONObject) versionsData.get(i);
+                versions[i] = createVersion(library, versionData);
+            }
+            sort(versions);
+            library.setVersions(versions);
+        } else {
+            library.setVersions(new Library.Version[0]);
+        }
+    }
+
     /**
-     * Search task - a task that performs one search for libraries matching
-     * the given search term.
+     * Sorts the library versions (in a descending order).
+     *
+     * @param versions versions to sort.
      */
-    final class SearchTask implements Runnable {
-        /** Name of the 'result' property. */
-        private static final String PROPERTY_RESULT = "results"; // NOI18N
-        /** Search term. */
-        private final String searchTerm;
-
-        /**
-         * Creates a new {@code SearchTask} for the given search term.
-         * 
-         * @param searchTerm search term.
-         */
-        SearchTask(String searchTerm) {
-            this.searchTerm = searchTerm;
+    private static void sort(Library.Version[] versions) {
+        Pair<Library.Version, Version>[] pairs = new Pair[versions.length];
+        for (int i = 0; i < versions.length; i++) {
+            Library.Version libraryVersion = versions[i];
+            Version version = Version.parse(libraryVersion.getName());
+            pairs[i] = Pair.of(libraryVersion, version);
         }
-
-        /**
-         * Returns the URL of the query that corresponds to the search
-         * for this search term.
-         * 
-         * @return URL of the query to perform.
-         */
-        String getSearchURL() {
-            String encodedSearchTerm;
-            try {
-                encodedSearchTerm = URLEncoder.encode(searchTerm, "UTF-8"); // NOI18N
-            } catch (UnsupportedEncodingException ueex) {
-                // Should not happen, UTF-8 should be supported everywhere
-                Logger.getLogger(LibraryProvider.class.getName()).log(Level.SEVERE, null, ueex);
-                encodedSearchTerm = searchTerm;
-            }
-            return SEARCH_URL_PREFIX + encodedSearchTerm;
+        Arrays.sort(pairs, VERSION_COMPARATOR);
+        for (int i = 0; i < versions.length; i++) {
+            versions[i] = pairs[i].first();
         }
+    }
 
-        @Override
-        public void run() {
-            String searchURL = getSearchURL();
-            String urlContent = readUrl(searchURL);
-            Library[] libraries = null;
-            if (urlContent != null) {
-                libraries = parse(urlContent);
-            }
-            updateCache(searchTerm, libraries);
-        }
+    /**
+     * Creates a library version for the given JSON data.
+     *
+     * @param library owning library.
+     * @param data    JSON data describing the library version.
+     *
+     * @return library version that corresponds to the given JSON data.
+     */
+    private static Library.Version createVersion(Library library, JSONObject data) {
+        Library.Version version = new Library.Version(library, false);
 
-        /**
-         * Reads the content of the given URL.
-         * 
-         * @param url URL whose content should be read.
-         * @return content of the given URL.
-         */
-        String readUrl(String url) {
-            String urlContent = null;
-            try {
-                URL urlObject = new URL(url);
-                URLConnection urlConnection = urlObject.openConnection();
-                StringBuilder content = new StringBuilder();
-                try (BufferedReader reader = new BufferedReader(new InputStreamReader(
-                        urlConnection.getInputStream(), "UTF-8"))) { // NOI18N
-                    String line;
-                    while ((line = reader.readLine()) != null) {
-                        content.append(line).append('\n');
-                    }
-                }
-                urlContent = content.toString();
-            } catch (MalformedURLException muex) {
-                Logger.getLogger(SearchTask.class.getName()).log(Level.INFO, null, muex);
-            } catch (IOException ioex) {
-                Logger.getLogger(SearchTask.class.getName()).log(Level.INFO, null, ioex);
-            }
-            return urlContent;
-        }
+        String versionName = (String) data.get(PROPERTY_VERSION_NAME);
+        version.setName(versionName);
 
-        /**
-         * Parses the given JSON result of the search.
-         * 
-         * @param data search result.
-         * @return libraries returned in the search result.
-         */
-        Library[] parse(String data) {
-            Library[] libraries = null;
-            try {
-                JSONParser parser = new JSONParser();
-                JSONObject searchResult = (JSONObject)parser.parse(data);
-                JSONArray libraryArray = (JSONArray)searchResult.get(PROPERTY_RESULT);
-                libraries = new Library[libraryArray.size()];
-                for (int i=0; i<libraries.length; i++) {
-                    JSONObject libraryData = (JSONObject)libraryArray.get(i);
-                    libraries[i] = createLibrary(libraryData);
-                }
-            } catch (ParseException pex) {
-                Logger.getLogger(SearchTask.class.getName()).log(Level.INFO, null, pex);
+        JSONArray filesData = (JSONArray) data.get(PROPERTY_FILES);
+        String[] files = new String[filesData.size()];
+        for (int i = 0; i < files.length; i++) {
+            Object fileInfo = filesData.get(i);
+            String fileName;
+            if (fileInfo instanceof JSONObject) {
+                JSONObject fileData = (JSONObject) fileInfo;
+                fileName = (String) fileData.get(PROPERTY_FILE_NAME);
+            } else {
+                fileName = fileInfo.toString();
             }
-            return libraries;
+            files[i] = fileName;
         }
-        
-        /** Name of the 'name' property. */
-        private static final String PROPERTY_NAME = "name"; // NOI18N
-        /** Name of the 'description' property. */
-        private static final String PROPERTY_DESCRIPTION = "description"; // NOI18N
-        /** Name of the 'homepage' property. */
-        private static final String PROPERTY_HOMEPAGE = "homepage"; // NOI18N
-        /** Name of the 'versions' property. */
-        private static final String PROPERTY_VERSIONS = "assets"; // NOI18N
-        /** Name of the 'version name' property. */
-        private static final String PROPERTY_VERSION_NAME = "version"; // NOI18N
-        /** Name of the 'files' property. */
-        private static final String PROPERTY_FILES = "files"; // NOI18N
-        /** Name of the 'file name' property. */
-        private static final String PROPERTY_FILE_NAME = "name"; // NOI18N
-
-        /**
-         * Creates a library for the given JSON data.
-         * 
-         * @param data JSON data describing the library.
-         * @return library that corresponds to the given JSON data.
-         */
-        Library createLibrary(JSONObject data) {
-            Library library = new Library();
-
-            String name = (String)data.get(PROPERTY_NAME);
-            library.setName(name);
-
-            String description = (String)data.get(PROPERTY_DESCRIPTION);
-            library.setDescription(description);
+        version.setFileInfo(files, null);
 
-            String homepage = (String)data.get(PROPERTY_HOMEPAGE);        
-            library.setHomePage(homepage);
+        return version;
+    }
 
-            JSONArray versionsData = (JSONArray)data.get(PROPERTY_VERSIONS);
-            Library.Version[] versions = new Library.Version[versionsData.size()];
-            for (int i=0; i<versions.length; i++) {
-                JSONObject versionData = (JSONObject)versionsData.get(i);
-                versions[i] = createVersion(library, versionData);
+    /**
+     * Reads the content of the given URL.
+     *
+     * @param url URL whose content should be read.
+     *
+     * @return content of the given URL.
+     */
+    static String readUrl(String url) {
+        String urlContent = null;
+        try {
+            URL urlObject = new URL(url);
+            URLConnection urlConnection = urlObject.openConnection();
+            StringBuilder content = new StringBuilder();
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(
+                urlConnection.getInputStream(), "UTF-8"))) { // NOI18N
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    content.append(line).append('\n');
+                }
             }
-            sort(versions);
-            library.setVersions(versions);
+            urlContent = content.toString();
+        } catch (MalformedURLException muex) {
+            Logger.getLogger(LibraryProvider.class.getName()).log(Level.INFO, null, muex);
+        } catch (IOException ioex) {
+            Logger.getLogger(LibraryProvider.class.getName()).log(Level.INFO, null, ioex);
+        }
+        return urlContent;
+    }
 
-            return library;
+    String getSearchURL(String searchTerm) {
+        String encodedSearchTerm;
+        try {
+            encodedSearchTerm = URLEncoder.encode(searchTerm, "UTF-8"); // NOI18N
+        } catch (UnsupportedEncodingException ueex) {
+            // Should not happen, UTF-8 should be supported everywhere
+            Logger.getLogger(LibraryProvider.class.getName()).log(Level.SEVERE, null, ueex);
+            encodedSearchTerm = searchTerm;
         }
+        return SEARCH_URL_PREFIX + encodedSearchTerm;
+    }
 
-        /**
-         * Sorts the library versions (in a descending order).
-         * 
-         * @param versions versions to sort.
-         */
-        private void sort(Library.Version[] versions) {
-            Pair<Library.Version,Version>[] pairs = new Pair[versions.length];
-            for (int i=0; i<versions.length; i++) {
-                Library.Version libraryVersion = versions[i];
-                Version version = Version.parse(libraryVersion.getName());
-                pairs[i] = Pair.of(libraryVersion, version);
-            }
-            Arrays.sort(pairs, VERSION_COMPARATOR);
-            for (int i=0; i<versions.length; i++) {
-                versions[i] = pairs[i].first();
+    /**
+     * Parses the given JSON result of the search.
+     *
+     * @param data search result.
+     *
+     * @return libraries returned in the search result.
+     */
+    Library[] parse(String data) {
+        Library[] libraries = null;
+        try {
+            JSONParser parser = new JSONParser();
+            JSONObject searchResult = (JSONObject) parser.parse(data);
+            JSONArray libraryArray = (JSONArray) searchResult.get(PROPERTY_RESULT);
+            libraries = new Library[libraryArray.size()];
+            for (int i = 0; i < libraries.length; i++) {
+                JSONObject libraryData = (JSONObject) libraryArray.get(i);
+                libraries[i] = createLibrary(libraryData);
             }
+        } catch (ParseException pex) {
+            Logger.getLogger(LibraryProvider.class.getName()).log(Level.INFO, null, pex);
         }
+        return libraries;
+    }
 
-        /**
-         * Creates a library version for the given JSON data.
-         * 
-         * @param library owning library.
-         * @param data JSON data describing the library version.
-         * @return library version that corresponds to the given JSON data.
-         */
-        private Library.Version createVersion(Library library, JSONObject data) {
-            Library.Version version = new Library.Version(library, false);
+    /**
+     * Creates a library for the given JSON data.
+     *
+     * @param data JSON data describing the library.
+     *
+     * @return library that corresponds to the given JSON data.
+     */
+    Library createLibrary(JSONObject data) {
+        Library library = new Library();
 
-            String versionName = (String)data.get(PROPERTY_VERSION_NAME);
-            version.setName(versionName);
+        updateLibrary(library, data);
 
-            JSONArray filesData = (JSONArray)data.get(PROPERTY_FILES);
-            String[] files = new String[filesData.size()];
-            for (int i=0; i<files.length; i++) {
-                Object fileInfo = filesData.get(i);
-                String fileName;
-                if (fileInfo instanceof JSONObject) {
-                    JSONObject fileData = (JSONObject)fileInfo;
-                    fileName = (String)fileData.get(PROPERTY_FILE_NAME);
-                } else {
-                    fileName = fileInfo.toString();
-                }
-                files[i] = fileName;
-            }
-            version.setFileInfo(files, null);
+        return library;
+    }
 
-            return version;
-        }
-        
+    void updateLibrary(Library library, JSONObject data) {
+        String name = (String) data.get(PROPERTY_NAME);
+        library.setName(name);
+
+        String description = (String) data.get(PROPERTY_DESCRIPTION);
+        library.setDescription(description);
+
+        String homepage = (String) data.get(PROPERTY_HOMEPAGE);
+        library.setHomePage(homepage);
+
+        extractVersionInformation(data, library);
     }
-    
 }
diff --git a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SearchPanel.java b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SearchPanel.java
index fdde532..f7a8131 100644
--- a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SearchPanel.java
+++ b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SearchPanel.java
@@ -23,9 +23,6 @@ import java.awt.Container;
 import java.awt.Cursor;
 import java.awt.Desktop;
 import java.awt.Dimension;
-import java.awt.EventQueue;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -42,12 +39,14 @@ import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JList;
 import javax.swing.ListModel;
+import javax.swing.SwingUtilities;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 import javax.swing.text.View;
 import org.netbeans.modules.javascript.cdnjs.Library;
 import org.netbeans.modules.javascript.cdnjs.LibraryProvider;
 import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
 
 /**
  * Panel for searching CDNJS libraries.
@@ -57,11 +56,11 @@ import org.openide.util.NbBundle;
 class SearchPanel extends javax.swing.JPanel {
     /** Minimum length of the text to be searched. */
     private static final int MIN_SEARCH_TEXT_LENGTH = 2;
-    /** Data listener for CDNJS provider. */
-    private final Listener listener = new Listener();
+    private static final RequestProcessor RP = new RequestProcessor(SearchPanel.class.getName(), 3);
     /** The last search term. */
     private String lastSearchTerm;
 
+
     /**
      * Creates a new {@code SearchPanel}.
      */
@@ -75,22 +74,6 @@ class SearchPanel extends javax.swing.JPanel {
         librarySelected(null);
     }
 
-    /**
-     * Activates this panel (i.e. registers all necessary listeners).
-     * This method should be called before the panel is shown to the user.
-     */
-    final void activate() {
-        LibraryProvider.getInstance().addPropertyChangeListener(listener);
-    }
-
-    /**
-     * Deactivates this panel (i.e. unregisters the listeners). This method
-     * should be called when the panel is no longer shown to the user.
-     */
-    final void deactivate() {
-        LibraryProvider.getInstance().removePropertyChangeListener(listener);
-    }
-
     private void initDocumentListener() {
         searchField.getDocument().addDocumentListener(new DocumentListener() {
             @Override
@@ -159,13 +142,12 @@ class SearchPanel extends javax.swing.JPanel {
         }
         librarySelected(null);
         lastSearchTerm = searchTerm;
-        Library[] libraries = LibraryProvider.getInstance().findLibraries(lastSearchTerm, Thread.MAX_PRIORITY);
-        if (libraries == null) {
-            messageLabel.setText(Bundle.SearchPanel_message_searching(lastSearchTerm));
-            showComponent(messageLabel);
-        } else {
-            updateLibraries(libraries);
-        }
+        messageLabel.setText(Bundle.SearchPanel_message_searching(lastSearchTerm));
+        showComponent(messageLabel);
+        RP.execute(() ->  {
+            Library[] libraries = LibraryProvider.getInstance().findLibraries(searchTerm);
+            SwingUtilities.invokeLater(() -> updateLibraries(libraries));
+        });
     }
 
     /**
@@ -554,8 +536,15 @@ class SearchPanel extends javax.swing.JPanel {
     }// </editor-fold>//GEN-END:initComponents
 
     private void librariesListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_librariesListValueChanged
-        Library library = librariesList.getSelectedValue();
-        librarySelected(library);
+        final Library library = librariesList.getSelectedValue();
+        if(library != null && (library.getVersions() == null || library.getVersions().length == 0)) {
+            RP.execute(() -> {
+                LibraryProvider.getInstance().updateLibraryVersions(library);
+                SwingUtilities.invokeLater(() -> librarySelected(library));
+            });
+        } else {
+            librarySelected(library);
+        }
     }//GEN-LAST:event_librariesListValueChanged
 
     private void searchFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchFieldActionPerformed
@@ -610,26 +599,6 @@ class SearchPanel extends javax.swing.JPanel {
     private javax.swing.JLabel versionLabel;
     // End of variables declaration//GEN-END:variables
 
-    /**
-     * Data listener for CDNJS provider.
-     */
-    class Listener implements PropertyChangeListener {
-
-        @Override
-        public void propertyChange(final PropertyChangeEvent evt) {
-            EventQueue.invokeLater(new Runnable() {
-                @Override
-                public void run() {
-                    String searchTerm = evt.getPropertyName();
-                    if (searchTerm.equals(lastSearchTerm)) {
-                        Library[] libraries = (Library[])evt.getNewValue();
-                        updateLibraries(libraries);
-                    }
-                }
-            });
-        }
-        
-    }
 
     /**
      * Renderer of {@code Library} objects.
diff --git a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SelectionPanel.java b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SelectionPanel.java
index 6e29f50..ed56be3 100644
--- a/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SelectionPanel.java
+++ b/webcommon/javascript.cdnjs/src/org/netbeans/modules/javascript/cdnjs/ui/SelectionPanel.java
@@ -20,8 +20,6 @@ package org.netbeans.modules.javascript.cdnjs.ui;
 
 import java.awt.Component;
 import java.awt.Dialog;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -32,6 +30,7 @@ import java.util.List;
 import java.util.Map;
 import javax.swing.JPanel;
 import javax.swing.JTable;
+import javax.swing.SwingUtilities;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import javax.swing.table.AbstractTableModel;
@@ -50,6 +49,7 @@ import org.openide.filesystems.FileChooserBuilder;
 import org.openide.util.HelpCtx;
 import org.openide.util.ImageUtilities;
 import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
 
 /**
  * Panel for customization of CDNJS libraries.
@@ -67,6 +67,7 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
     private final File webRoot;
     /** Maps the name of the library to its CDNJS meta-data. */
     private final Map<String,Library> libraryInfo = new HashMap<>();
+    private static final RequestProcessor RP = new RequestProcessor(SearchPanel.class.getName(), 3);
 
     /**
      * Creates a new {@code SelectionPanel}.
@@ -122,7 +123,6 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
     @NbBundle.Messages({"SelectionPanel.searchDialog.title=Add CDNJS Library"})
     private void showSearchPanel() {
         SearchPanel panel = getSearchPanel();
-        panel.activate();
         DialogDescriptor descriptor = new DialogDescriptor(
                 panel,
                 Bundle.SelectionPanel_searchDialog_title(),
@@ -138,7 +138,6 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
         );
         Dialog dialog = DialogDisplayer.getDefault().createDialog(descriptor);
         dialog.setVisible(true);
-        panel.deactivate();
         if (descriptor.getValue() == panel.getAddButton()) {
             Library.Version selectedVersion = panel.getSelectedVersion();
             if (selectedVersion != null) {
@@ -269,36 +268,21 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
     }
 
     /**
-     * Releases resources and unregisters the listeners used by this panel.
-     */
-    public void dispose() {
-        LibraryProvider.getInstance().removePropertyChangeListener(libraryInfoListener);
-    }
-
-    /** Listener on the CDNJS library provider. */
-    private PropertyChangeListener libraryInfoListener;
-
-    /**
      * Loads the CDNJS meta-data about the existing libraries.
      * 
      * @param existingLibraries libraries already present in the project.
      */
     private void loadLibraryInfo(final Library.Version[] existingLibraries) {
-        LibraryProvider provider = LibraryProvider.getInstance();
-        libraryInfoListener = new PropertyChangeListener() {
-            @Override
-            public void propertyChange(PropertyChangeEvent evt) {
-                updateLibraryInfo(evt.getPropertyName(), (Library[])evt.getNewValue());
-            }
-        };
-        provider.addPropertyChangeListener(libraryInfoListener);
-        for (Library.Version libraryVersion : existingLibraries) {
-            String libraryName = libraryVersion.getLibrary().getName();
-            Library[] foundLibraries = provider.findLibraries(libraryName, Thread.MIN_PRIORITY);
-            if (foundLibraries != null) {
-                updateLibraryInfo(libraryName, foundLibraries);
+        RP.execute(() -> {
+            LibraryProvider provider = LibraryProvider.getInstance();
+            for (Library.Version libraryVersion : existingLibraries) {
+                String libraryName = libraryVersion.getLibrary().getName();
+                Library library = provider.loadLibrary(libraryName);
+                if (library != null) {
+                    SwingUtilities.invokeLater(() -> updateLibraryInfo(library));
+                }
             }
-        }
+        });
     }
 
     /**
@@ -307,17 +291,8 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
      * @param libraryName name of the library.
      * @param foundLibraries search result for a search term equal to the name of the library.
      */
-    void updateLibraryInfo(String libraryName, Library[] foundLibraries) {
-        if (foundLibraries == null) {
-            libraryInfo.put(libraryName, null);
-        } else {
-            for (Library library : foundLibraries) {
-                if (libraryName.equals(library.getName())) {
-                    libraryInfo.put(libraryName, library);
-                    break;
-                }
-            }
-        }
+    void updateLibraryInfo(Library foundLibrary) {
+        libraryInfo.put(foundLibrary.getName(), foundLibrary);
         tableModel.fireTableDataChanged();
     }
 
@@ -670,7 +645,7 @@ public class SelectionPanel extends JPanel implements HelpCtx.Provider {
                 case 2:
                     String libraryName = libraryVersion.getLibrary().getName();
                     Library library = libraryInfo.get(libraryName);
-                    if (library == null) {
+                    if (library == null || library.getVersions() == null || library.getVersions().length == 0) {
                         value = libraryInfo.containsKey(libraryName)
                                 ? VersionColumnRenderer.UNKNOWN
                                 : VersionColumnRenderer.CHECKING;
diff --git a/webcommon/javascript.cdnjs/test/unit/src/org/netbeans/modules/javascript/cdnjs/LibraryProviderTest.java b/webcommon/javascript.cdnjs/test/unit/src/org/netbeans/modules/javascript/cdnjs/LibraryProviderTest.java
index c31b211..e9d2e59 100644
--- a/webcommon/javascript.cdnjs/test/unit/src/org/netbeans/modules/javascript/cdnjs/LibraryProviderTest.java
+++ b/webcommon/javascript.cdnjs/test/unit/src/org/netbeans/modules/javascript/cdnjs/LibraryProviderTest.java
@@ -18,10 +18,8 @@
  */
 package org.netbeans.modules.javascript.cdnjs;
 
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.netbeans.junit.NbTestCase;
-import org.netbeans.junit.RandomlyFails;
 
 /**
  * Tests of a {@code LibraryProvider} class.
@@ -34,38 +32,37 @@ public class LibraryProviderTest extends NbTestCase {
         super(name);
     }
 
-    @BeforeClass
-    public static void initProxy() {
-        System.setProperty("http.proxyHost", "www-proxy.uk.oracle.com"); // NOI18N
-        System.setProperty("http.proxyPort", "80"); // NOI18N
-        System.setProperty("https.proxyHost", "www-proxy.uk.oracle.com"); // NOI18N
-        System.setProperty("https.proxyPort", "80"); // NOI18N
-    }
-
     /**
      * Tests whether the CDNJS server returns the content with the expected
      * structure. So, it is more a sanity check of the CDNJS service.
      * then a test of the class {@code LibraryProvider} itself.
      */
-    @Test
-    @RandomlyFails
-    public void testCDNJSResponseStructure() {
+    public void testCDNJSResponseSearchStructure() {
         String searchTerm = "knockout"; // NOI18N
         LibraryProvider provider = LibraryProvider.getInstance();
-        LibraryProvider.SearchTask task = provider.new SearchTask(searchTerm);
-        String searchURL = task.getSearchURL();
-        String data = task.readUrl(searchURL);
-        assertNotNull(data);
+        Library[] libraries = provider.findLibraries(searchTerm);
 
-        Library[] libraries = task.parse(data);
         assertNotNull(libraries);
+        assertTrue(libraries.length > 0);
 
-        Library knockoutLibrary = null;
-        for (Library library : libraries) {
-            if (searchTerm.equals(library.getName())) {
-                knockoutLibrary = library;
-            }
+        for(Library library: libraries) {
+            assertNotNull(library.getVersions());
+            assertEquals(0, library.getVersions().length);
         }
+    }
+
+    /**
+     * Tests whether the CDNJS server returns the content with the expected
+     * structure. So, it is more a sanity check of the CDNJS service.
+     * then a test of the class {@code LibraryProvider} itself.
+     */
+    @Test
+    public void testCDNJSResponseLibraryStructure() {
+        String searchTerm = "knockout"; // NOI18N
+        LibraryProvider provider = LibraryProvider.getInstance();
+        Library knockoutLibrary = provider.loadLibrary(searchTerm);
+        assertNotNull(knockoutLibrary);
+
         assertNotNull(knockoutLibrary);
         assertNotNull(knockoutLibrary.getDescription());
         assertNotNull(knockoutLibrary.getHomePage());
@@ -74,6 +71,9 @@ public class LibraryProviderTest extends NbTestCase {
         assertNotNull(versions);
         assertTrue(versions.length > 0);
 
+        provider.updateLibraryVersions(knockoutLibrary);
+        versions = knockoutLibrary.getVersions();
+
         Library.Version version = versions[0];
         assertNotNull(version);
         assertNotNull(version.getName());


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists