Android Open Source - SimpleReader R S S Loader






From Project

Back to project page SimpleReader.

License

The source code is released under:

Apache License

If you think the Android project SimpleReader listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2011 A. Horn/*from  w  w  w.j av  a  2 s  .c o  m*/
 *
 * 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 com.dreamteam.app.rss;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Asynchronous loader for RSS feeds. RSS feeds can be loaded in FIFO order or
 * based on priority. Objects of this type can be constructed with one of the
 * provided static methods:
 * <ul>
 * <li>{@link #fifo()}</li>
 * <li>{@link #fifo(int)}</li>
 * <li>{@link #priority()}</li>
 * <li>{@link #priority(int)}</li>
 * </ul>
 * 
 * Completed RSS feed loads can be retrieved with {@link RSSLoader#take()},
 * {@link RSSLoader#poll()} or {@link RSSLoader#poll(long, TimeUnit)}.
 * 
 * <p>
 * <b>Usage Example</b>
 * 
 * Suppose you want to load an array of RSS feed URIs concurrently before
 * retrieving the results one at a time. You could write this as:
 * 
 * <pre>
 * {@code 
 *  void fetchRSS(String[] uris) throws InterruptedException {
 *     RSSLoader loader = RSSLoader.fifo();
 *     for (String uri : uris) {
 *       loader.load(uri);
 *     }
 *     
 *     Future&lt;RSSFeed&gt; future;
 *     RSSFeed feed;
 *     for (int i = 0; i &lt; uris.length; i++) {
 *       future = loader.take();
 *       try {
 *         feed = future.get();
 *         use(feed);
 *       } catch (ExecutionException ignore) {}
 *     }
 * }}
 * </pre>
 *
 * </p>
 * 
 * @author A. Horn
 */
public class RSSLoader {

  /**
   * Human-readable name of the thread loading RSS feeds
   */
  private final static String DEFAULT_THREAD_NAME = "Asynchronous RSS feed loader";

  /**
   * Arrange incoming load requests on this queue.
   */
  private final BlockingQueue<RSSFuture> in;

  /**
   * Once the an RSS feed has completed loading, place the result on this queue.
   */
  private final BlockingQueue<RSSFuture> out;

  /**
   * Flag changes are visible after operations on {@link #in} queue.
   */
  private boolean stopped;

  /**
   * Create an object which can load RSS feeds asynchronously in FIFO order.
   * 
   * @see #fifo(int)
   */
  public static RSSLoader fifo() {
    return new RSSLoader(new LinkedBlockingQueue<RSSFuture>());
  }

  /**
   * Create an object which can load RSS feeds asynchronously in FIFO order.
   * 
   * @param capacity
   *          expected number of URIs to be loaded at a given time
   */
  public static RSSLoader fifo(int capacity) {
    return new RSSLoader(new LinkedBlockingQueue<RSSFuture>(capacity));
  }

  /**
   * Create an object which can load RSS feeds asynchronously based on priority.
   * 
   * @see #priority(int)
   */
  public static RSSLoader priority() {
    return new RSSLoader(new PriorityBlockingQueue<RSSFuture>());
  }

  /**
   * Create an object which can load RSS feeds asynchronously based on priority.
   * 
   * @param capacity
   *          expected number of URIs to be loaded at a given time
   */
  public static RSSLoader priority(int capacity) {
    return new RSSLoader(new PriorityBlockingQueue<RSSFuture>(capacity));
  }

  /**
   * Instantiate an object which can load RSS feeds asynchronously. The provided
   * {@link BlockingQueue} implementation determines the load behaviour.
   * 
   * @see LinkedBlockingQueue
   * @see PriorityBlockingQueue
   */
  RSSLoader(BlockingQueue<RSSFuture> in) {
    this.in = in;
    this.out = new LinkedBlockingQueue<RSSFuture>();

    // start separate thread for loading of RSS feeds
    new Thread(new Loader(new RSSReader()), DEFAULT_THREAD_NAME).start();
  }

  /**
   * Returns {@code true} if RSS feeds are currently being loaded, {@code false}
   * otherwise.
   */
  public boolean isLoading() {
    // order of conjuncts matters because of happens-before relationship
    return !in.isEmpty() && !stopped;
  }

  /**
   * Stop thread after finishing loading pending RSS feed URIs. If this loader
   * has been constructed with {@link #priority()} or {@link #priority(int)},
   * only RSS feed loads with priority strictly greater than seven (7) are going
   * to be completed.
   * <p>
   * Subsequent invocations of {@link #load(String)} and
   * {@link #load(String, int)} return {@code null}.
   */
  public void stop() {
    // flag writings happen-before enqueue
    stopped = true;
    in.offer(SENTINEL);
  }

  /**
   * Loads the specified RSS feed URI asynchronously. If this loader has been
   * constructed with {@link #priority()} or {@link #priority(int)}, then a
   * default priority of three (3) is used. Otherwise, RSS feeds are loaded in
   * FIFO order.
   * <p>
   * Returns {@code null} if the RSS feed URI cannot be scheduled for loading
   * due to resource constraints or if {@link #stop()} has been previously
   * called.
   * <p>
   * Completed RSS feed loads can be retrieved by calling {@link #take()}.
   * Alternatively, non-blocking polling is possible with {@link #poll()}.
   * 
   * @param uri
   *          RSS feed URI to be loaded
   * 
   * @return Future representing the RSS feed scheduled for loading,
   *         {@code null} if scheduling failed
   */
  public Future<RSSFeed> load(String uri) {
    return load(uri, RSSFuture.DEFAULT_PRIORITY);
  }

  /**
   * Loads the specified RSS feed URI asynchronously. For the specified priority
   * to determine the relative loading order of RSS feeds, this loader must have
   * been constructed with {@link #priority()} or {@link #priority(int)}.
   * Otherwise, RSS feeds are loaded in FIFO order.
   * <p>
   * Returns {@code null} if the RSS feed URI cannot be scheduled for loading
   * due to resource constraints or if {@link #stop()} has been previously
   * called.
   * <p>
   * Completed RSS feed loads can be retrieved by calling {@link #take()}.
   * Alternatively, non-blocking polling is possible with {@link #poll()}.
   * 
   * @param uri
   *          RSS feed URI to be loaded
   * @param priority
   *          larger integer gives higher priority
   * 
   * @return Future representing the RSS feed scheduled for loading,
   *         {@code null} if scheduling failed
   */
  public Future<RSSFeed> load(String uri, int priority) {
    if (uri == null) {
      throw new IllegalArgumentException("RSS feed URI must not be null.");
    }

    // optimization (after flag changes have become visible)
    if (stopped) {
      return null;
    }

    // flag readings happen-after enqueue
    final RSSFuture future = new RSSFuture(uri, priority);
    final boolean ok = in.offer(future);

    if (!ok || stopped) {
      return null;
    }

    return future;
  }

  /**
   * Retrieves and removes the next Future representing the result of loading an
   * RSS feed, waiting if none are yet present.
   * 
   * @return the {@link Future} representing the loaded RSS feed
   * 
   * @throws InterruptedException
   *           if interrupted while waiting
   */
  public Future<RSSFeed> take() throws InterruptedException {
    return out.take();
  }

  /**
   * Retrieves and removes the next Future representing the result of loading an
   * RSS feed or {@code null} if none are present.
   * 
   * @return the {@link Future} representing the loaded RSS feed, or
   *         {@code null} if none are present
   * 
   * @throws InterruptedException
   *           if interrupted while waiting
   */
  public Future<RSSFeed> poll() {
    return out.poll();
  }

  /**
   * Retrieves and removes the Future representing the result of loading an RSS
   * feed, waiting if necessary up to the specified wait time if none are yet
   * present.
   * 
   * @param timeout
   *          how long to wait before giving up, in units of {@code unit}
   * @param unit
   *          a {@link TimeUnit} determining how to interpret the
   *          {@code timeout} parameter
   * @return the {@link Future} representing the loaded RSS feed, or
   *         {@code null} if none are present within the specified time interval
   * @throws InterruptedException
   *           if interrupted while waiting
   */
  public Future<RSSFeed> poll(long timeout, TimeUnit unit) throws InterruptedException {
    return out.poll(timeout, unit);
  }

  /**
   * Internal consumer of RSS feed URIs stored in the blocking queue.
   */
  class Loader implements Runnable {

    private final RSSReader reader;

    Loader(RSSReader reader) {
      this.reader = reader;
    }

    /**
     * Keep on loading RSS feeds by dequeuing incoming tasks until the sentinel
     * is encountered.
     */
    @Override
    public void run() {
      try {
        RSSFuture future = null;
        RSSFeed feed;
        while ((future = in.take()) != SENTINEL) {

          if (future.status.compareAndSet(RSSFuture.READY, RSSFuture.LOADING)) {
            try {
              // perform loading outside of locked region
              feed = reader.load(future.uri);

              // set successfully loaded RSS feed
              future.set(feed, /* error */null);

              // enable caller to consume the loaded RSS feed
              out.add(future);
            } catch (RSSException e) {
              // throw ExecutionException when calling RSSFuture::get()
              future.set(/* feed */null, e);
            } catch (RSSFault e) {
              // throw ExecutionException when calling RSSFuture::get()
              future.set(/* feed */null, e);
            } finally {
              // RSSFuture::isDone() returns true even if an error occurred
              future.status.compareAndSet(RSSFuture.LOADING, RSSFuture.LOADED);
            }
          }

        }
      } catch (InterruptedException e) {
        // Restore the interrupted status
        Thread.currentThread().interrupt();
      }
    }

  }

  /**
   * Internal sentinel to stop the thread that is loading RSS feeds.
   */
  private final static RSSFuture SENTINEL = new RSSFuture(null, /* priority */7);

  /**
   * Offer callers control over the asynchronous loading of an RSS feed.
   */
  static class RSSFuture implements Future<RSSFeed>, Comparable<RSSFuture> {

    static final int DEFAULT_PRIORITY = 3;
    static final int READY = 0;
    static final int LOADING = 1;
    static final int LOADED = 2;
    static final int CANCELLED = 4;

    /** RSS feed URI */
    final String uri;

    /** Larger integer gives higher priority */
    final int priority;

    AtomicInteger status;

    boolean waiting;
    RSSFeed feed;
    Exception cause;

    RSSFuture(String uri, int priority) {
      this.uri = uri;
      this.priority = priority;
      status = new AtomicInteger(READY);
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
      return isCancelled() || status.compareAndSet(READY, CANCELLED);
    }

    @Override
    public boolean isCancelled() {
      return status.get() == CANCELLED;
    }

    @Override
    public boolean isDone() {
      return (status.get() & (LOADED | CANCELLED)) != 0;
    }

    @Override
    public synchronized RSSFeed get() throws InterruptedException, ExecutionException {
      if (feed == null && cause == null) {
        try {
          waiting = true;

          // guard against spurious wakeups
          while (waiting) {
            wait();
          }
        } finally {
          waiting = false;
        }
      }

      if (cause != null) {
        throw new ExecutionException(cause);
      }

      return feed;
    }

    @Override
    public synchronized RSSFeed get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {

      if (feed == null && cause == null) {
        try {
          waiting = true;

          final long timeoutMillis = unit.toMillis(timeout);
          final long startMillis = System.currentTimeMillis();

          // guard against spurious wakeups
          while (waiting) {
            wait(timeoutMillis);

            // check timeout
            if (System.currentTimeMillis() - startMillis > timeoutMillis) {
              throw new TimeoutException("RSS feed loading timed out");
            }
          }
        } finally {
          waiting = false;
        }
      }

      if (cause != null) {
        throw new ExecutionException(cause);
      }

      return feed;
    }

    synchronized void set(RSSFeed feed, Exception cause) {
      this.feed = feed;
      this.cause = cause;

      if (waiting) {
        waiting = false;
        notifyAll();
      }
    }

    @Override
    public int compareTo(RSSFuture other) {
      // Note: head of PriorityQueue implementation is the least element
      return other.priority - priority;
    }
  }

}




Java Source Code List

com.dreamteam.app.adapter.CategoryDetailAdapter.java
com.dreamteam.app.adapter.FeedCategoryAdapter.java
com.dreamteam.app.adapter.GridAdapter.java
com.dreamteam.app.adapter.GuideViewPagerAdapter.java
com.dreamteam.app.adapter.ItemListAdapter.java
com.dreamteam.app.adapter.MPagerAdapter.java
com.dreamteam.app.commons.AppConfig.java
com.dreamteam.app.commons.AppContext.java
com.dreamteam.app.commons.HtmlFilter.java
com.dreamteam.app.commons.IFlyHelper.java
com.dreamteam.app.commons.ItemListEntityParser.java
com.dreamteam.app.commons.SectionHelper.java
com.dreamteam.app.commons.SeriaHelper.java
com.dreamteam.app.commons.SkinManager.java
com.dreamteam.app.commons.UIHelper.java
com.dreamteam.app.config.Contants.java
com.dreamteam.app.db.DbManager.java
com.dreamteam.app.db.FavoItemDbHelper.java
com.dreamteam.app.db.FeedDBManager.java
com.dreamteam.app.db.provider.RSSFeedCategoryProvider.java
com.dreamteam.app.entity.FeedItem.java
com.dreamteam.app.entity.Feed.java
com.dreamteam.app.entity.ItemListEntity.java
com.dreamteam.app.entity.RSSFeedCategroy.java
com.dreamteam.app.entity.Section.java
com.dreamteam.app.img.FileCacheManager.java
com.dreamteam.app.img.FileCache.java
com.dreamteam.app.img.ICache.java
com.dreamteam.app.img.ImageLoadTask.java
com.dreamteam.app.img.ImageLoad.java
com.dreamteam.app.img.ImageLoader.java
com.dreamteam.app.img.MemoryCache.java
com.dreamteam.app.rss.Dates.java
com.dreamteam.app.rss.Integers.java
com.dreamteam.app.rss.MediaAttributes.java
com.dreamteam.app.rss.MediaEnclosure.java
com.dreamteam.app.rss.MediaThumbnail.java
com.dreamteam.app.rss.RSSBase.java
com.dreamteam.app.rss.RSSConfig.java
com.dreamteam.app.rss.RSSException.java
com.dreamteam.app.rss.RSSFault.java
com.dreamteam.app.rss.RSSFeed.java
com.dreamteam.app.rss.RSSHandler.java
com.dreamteam.app.rss.RSSItem.java
com.dreamteam.app.rss.RSSLoader.java
com.dreamteam.app.rss.RSSParserSPI.java
com.dreamteam.app.rss.RSSParser.java
com.dreamteam.app.rss.RSSReaderException.java
com.dreamteam.app.rss.RSSReader.java
com.dreamteam.app.rss.Resources.java
com.dreamteam.app.ui.About.java
com.dreamteam.app.ui.BaseActivity.java
com.dreamteam.app.ui.BaseTitledActivity.java
com.dreamteam.app.ui.CategoryDetailActivity.java
com.dreamteam.app.ui.ColorListActivity.java
com.dreamteam.app.ui.FavoriteItemList.java
com.dreamteam.app.ui.FeedCategoryActivity.java
com.dreamteam.app.ui.FeedbackUI.java
com.dreamteam.app.ui.GuideActivity.java
com.dreamteam.app.ui.ImageDialog.java
com.dreamteam.app.ui.ImagesBrowseActivity.java
com.dreamteam.app.ui.ItemDetailActivity.java
com.dreamteam.app.ui.ItemListActivity.java
com.dreamteam.app.ui.LocalImageBrowseActivity.java
com.dreamteam.app.ui.LoginDialog.java
com.dreamteam.app.ui.MainActivity.java
com.dreamteam.app.ui.Setting.java
com.dreamteam.app.ui.SplashActivity.java
com.dreamteam.app.ui.SwitchBgActivity.java
com.dreamteam.app.ui.WidgetProvider.java
com.dreamteam.app.ui.adapter.ColorListAdapter.java
com.dreamteam.app.utils.CategoryNameExchange.java
com.dreamteam.app.utils.DateUtils.java
com.dreamteam.app.utils.FileUtils.java
com.dreamteam.app.utils.HttpUtils.java
com.dreamteam.app.utils.ImageLoader.java
com.dreamteam.app.utils.ImageUtils.java
com.dreamteam.app.utils.Logger.java
com.dreamteam.app.utils.MD5.java
com.dreamteam.app.utils.StringUtils.java
com.dreamteam.app.wallpaper.ChildAdapter.java
com.dreamteam.app.wallpaper.GroupGridAdapter.java
com.dreamteam.app.wallpaper.ImageBean.java
com.dreamteam.app.wallpaper.MyImageView.java
com.dreamteam.app.wallpaper.NativeImageLoader.java
com.dreamteam.app.wallpaper.WallPaperManager.java
com.dreamteam.custom.ui.PathAnimations.java
com.dreamteam.custom.ui.PullToRefreshListView.java