Newsfeed: use async DNS resolution and News fetch 37/20837/5
Clement DAVID [Tue, 12 Feb 2019 10:27:31 +0000 (11:27 +0100)]
On laggy DNS resolution, Scilab had a laggy startup. This commit
delegate the openConnection and News fetching to a SwingWorker thread
which will free the EDT (main Scilab UI) to do its rendering job.

Change-Id: Ibae3db48abcfa3083e0ab2bd0ae8f1573d6da650

scilab/modules/ui_data/src/java/org/scilab/modules/ui_data/newsfeed/NewsFeedController.java
scilab/modules/ui_data/src/java/org/scilab/modules/ui_data/newsfeed/NewsFeedWidget.java
scilab/modules/ui_data/src/java/org/scilab/modules/ui_data/newsfeed/NewsFetcher.java

index e068e05..66a469f 100644 (file)
@@ -165,7 +165,7 @@ public class NewsFeedController implements ActionListener {
     }
 
     public void updateNewsFeed() {
-        news = null;
+        news = new ArrayList<>();
 
         try {
             newsFetcher.readSettings();
@@ -175,7 +175,7 @@ public class NewsFeedController implements ActionListener {
             }
 
             // TODO : update only if RSS feed has new news
-            news = newsFetcher.fetchNews();
+            newsFetcher.fetchNews(this, news);
         } catch (Exception e) {
             System.err.println(e);
             fireNewsFeedErrorEvent(NewsFeedUIMessages.NEWS_FEED_UNAVAILABLE_ERROR);
index c91270c..3f026f8 100644 (file)
@@ -107,8 +107,11 @@ public class NewsFeedWidget extends JPanel implements NewsFeedEventListener, Hyp
         setLayout(new BorderLayout());\r
         add(headerPane, BorderLayout.NORTH);\r
         add(scrollPane, BorderLayout.CENTER);\r
+        \r
+        // at startup, display an error message\r
+        displayError(NewsFeedUIMessages.NEWS_FEED_UNAVAILABLE_ERROR);\r
     }\r
-\r
+    \r
     public void newsFeedEventReceived(NewsFeedEvent evt) {\r
         switch (evt.getEventType()) {\r
             case NewsFeedEvent.NEWS_CHANGED: {\r
index 21653c1..c2654e4 100644 (file)
@@ -30,13 +30,91 @@ import java.util.Locale;
 \r
 import java.util.List;\r
 import java.util.ArrayList;\r
+import javax.swing.SwingWorker;\r
 \r
 /**\r
  * Fetches news through a RSS feed\r
  * The RSS URL is configurable in the settings file\r
  */\r
 public class NewsFetcher {\r
+    private static class Worker extends SwingWorker<Object, News> {\r
+        private final NewsFeedController controller;\r
+        private final URL rssURL;\r
+        private final List<News> into;\r
+\r
+        private Worker(NewsFeedController controller, URL rssURL, List<News> into) {\r
+            this.controller = controller;\r
+            this.rssURL = rssURL;\r
+            this.into = into;\r
+        }\r
+\r
+        @Override\r
+        protected void process(List<News> chunks) {\r
+            into.addAll(chunks);\r
+        }\r
+\r
+        @Override\r
+        protected void done() {\r
+            controller.nextNews();\r
+        }\r
+\r
+        @Override\r
+        protected Object doInBackground() throws Exception {\r
+            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();\r
+            Document doc = builder.parse(rssURL.openStream());\r
+\r
+            NodeList items = doc.getElementsByTagName("item");\r
+            if ((items == null) || (items.getLength() == 0)) {\r
+                throw new Exception("The fetched document has no 'item' element, please check it is a RSS feed.");\r
+            }\r
+\r
+            SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);\r
+\r
+            for (int i = 0; i < items.getLength(); i++) {\r
+                Element item = (Element) items.item(i);\r
+                String title = getItemValue(item, "title");\r
+                String dateStr = getItemValue(item, "pubDate");\r
+                Date date = formatter.parse(dateStr);\r
+                String description = getItemValue(item, "description");\r
+                String content = getItemValue(item, "content:encoded");\r
+                // media content\r
+                NewsMediaContent mediaContent = null;\r
+                Node mediaContentNode = getItemNode(item, "media:content");\r
+                if (mediaContentNode != null) {\r
+                    String url = mediaContentNode.getAttributes().getNamedItem("url").getNodeValue();\r
+                    String width = mediaContentNode.getAttributes().getNamedItem("width").getNodeValue();\r
+                    String height = mediaContentNode.getAttributes().getNamedItem("height").getNodeValue();\r
+                    mediaContent = new NewsMediaContent(url, width, height);\r
+                }\r
+                String link = getItemValue(item, "link");\r
+                publish(new News(title, date, description, content, mediaContent, link));\r
+            }\r
+\r
+            return null;\r
+        }\r
+\r
+        private static Node getItemNode(Element item, String nodeName) {\r
+            NodeList nodeList = item.getElementsByTagName(nodeName);\r
+            if (nodeList.getLength() > 0) {\r
+                return nodeList.item(0);\r
+            } else {\r
+                return null;\r
+            }\r
+        }\r
+\r
+        private static String getItemValue(Element item, String nodeName) {\r
+            Node node = getItemNode(item, nodeName);\r
+            if (node != null) {\r
+                if (node.hasChildNodes()) {\r
+                    return node.getFirstChild().getNodeValue();\r
+                }\r
+            }\r
+            return null;\r
+        }\r
+    }\r
+\r
     private URL rssURL = null;\r
+    private Worker worker = null;\r
 \r
     public NewsFetcher() {\r
     }\r
@@ -56,57 +134,16 @@ public class NewsFetcher {
         return rssURL != null;\r
     }\r
 \r
-    public List<News> fetchNews() throws Exception {\r
-        List<News> newsList = new ArrayList<News>();\r
-        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();\r
-        Document doc = builder.parse(rssURL.openStream());\r
-\r
-        NodeList items = doc.getElementsByTagName("item");\r
-        if ((items == null) || (items.getLength() == 0)) {\r
-            throw new Exception("The fetched document has no 'item' element, please check it is a RSS feed.");\r
-        }\r
-\r
-        SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);\r
-\r
-        for (int i = 0; i < items.getLength(); i++) {\r
-            Element item = (Element) items.item(i);\r
-            String title = getItemValue(item, "title");\r
-            String dateStr = getItemValue(item, "pubDate");\r
-            Date date = formatter.parse(dateStr);\r
-            String description = getItemValue(item, "description");\r
-            String content = getItemValue(item, "content:encoded");\r
-            // media content\r
-            NewsMediaContent mediaContent = null;\r
-            Node mediaContentNode = getItemNode(item, "media:content");\r
-            if (mediaContentNode != null) {\r
-                String url = mediaContentNode.getAttributes().getNamedItem("url").getNodeValue();\r
-                String width = mediaContentNode.getAttributes().getNamedItem("width").getNodeValue();\r
-                String height = mediaContentNode.getAttributes().getNamedItem("height").getNodeValue();\r
-                mediaContent = new NewsMediaContent(url, width, height);\r
-            }\r
-            String link = getItemValue(item, "link");\r
-            newsList.add(new News(title, date, description, content, mediaContent, link));\r
-        }\r
-        return newsList;\r
-    }\r
-\r
-    private Node getItemNode(Element item, String nodeName) {\r
-        NodeList nodeList = item.getElementsByTagName(nodeName);\r
-        if (nodeList.getLength() > 0) {\r
-            return nodeList.item(0);\r
-        } else {\r
-            return null;\r
-        }\r
-    }\r
-\r
-    private String getItemValue(Element item, String nodeName) {\r
-        Node node = getItemNode(item, nodeName);\r
-        if (node != null) {\r
-            if (node.hasChildNodes()) {\r
-                return node.getFirstChild().getNodeValue();\r
-            }\r
+    public void fetchNews(NewsFeedController controller, List<News> into) throws Exception {\r
+        // if already in progress discard further update in case of laggy \r
+        // DNS or HTTP GET.\r
+        if (worker != null) {\r
+            worker.cancel(true);\r
         }\r
-        return null;\r
+        \r
+        // setup the fetch\r
+        worker = new Worker(controller, rssURL, into);\r
+        worker.execute();\r
     }\r
 \r
 }\r