All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.tidalwave.bluebill.mobile.news.DefaultNewsController Maven / Gradle / Ivy

There is a newer version: 1.0.15
Show newest version
/***********************************************************************************************************************
 *
 * blueBill Mobile - open source birdwatching
 * ==========================================
 *
 * Copyright (C) 2009, 2010 by Tidalwave s.a.s. (http://www.tidalwave.it)
 * http://bluebill.tidalwave.it/mobile/
 *
 ***********************************************************************************************************************
 *
 * 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.
 *
 ***********************************************************************************************************************
 *
 * $Id: DefaultNewsController.java,v 70fb0b4b2a54 2010/07/04 21:14:16 fabrizio $
 *
 **********************************************************************************************************************/
package it.tidalwave.bluebill.mobile.news;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.inject.Provider;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import it.tidalwave.util.Id;
import it.tidalwave.util.NotFoundException;
import it.tidalwave.util.logging.Logger;
import it.tidalwave.netbeans.util.Locator;
import it.tidalwave.mobile.io.FileSystem;
import it.tidalwave.mobile.io.IoUtils;
import it.tidalwave.mobile.io.MasterFileSystem;
import it.tidalwave.mobile.rss.Message;
import it.tidalwave.mobile.rss.RSSFeed;
import it.tidalwave.mobile.rss.RSSFeedReader;
import it.tidalwave.bluebill.mobile.network.NetworkingPreferences;
import it.tidalwave.mobile.io.StringsIO;
import static it.tidalwave.mobile.rss.Vocabulary.*;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 * @version $Id: $
 *
 **********************************************************************************************************************/
@ServiceProvider(service=NewsController.class)
public class DefaultNewsController implements NewsController
  {
    private static final String CLASS = DefaultNewsController.class.getName();
    private static final Logger logger = Logger.getLogger(CLASS);

    private static final String NEWS_FEED_URL = "http://bluebill.tidalwave.it/mobile/blog.rss";

    private final static long ONE_DAY = 24 * 60 * 60 * 1000;
    private final static long ONE_MINUTE = 60 * 1000;

    protected enum Status
      {
        NEEDS_DOWNLOAD,
        DOWNLOADING,
        DOWNLOADED
      }

    @Nonnegative
    protected long refreshPeriod = ONE_DAY;

    @Nonnull
    protected Status status = Status.NEEDS_DOWNLOAD;

    @CheckForNull
    protected Thread thread;

    @Nonnull
    protected final File cachedFile;

    @Nonnull
    protected final File readLogFile;

    @CheckForNull
    protected RSSFeed rssFeed;

    @Nonnull
    private final Provider masterFileSystem = Locator.createProviderFor(MasterFileSystem.class);

    @Nonnull
    private final Provider networkingPreferences = Locator.createProviderFor(NetworkingPreferences.class);

    @CheckForNull
    protected Thread thread1;

    private final Set readMessages = new TreeSet();

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public DefaultNewsController()
      throws IOException
      {
        final FileSystem internalFileSystem = masterFileSystem.get().getInternalFileSystem();
        cachedFile = internalFileSystem.getFile("blog.rss");
        readLogFile = internalFileSystem.getFile("blog-read-messages.txt");
        loadReadMessagesLog();
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void start()
      {
        logger.info("start()");
        
        if (thread == null)
          {
            if (!networkingPreferences.get().isNetworkConnectionAllowed())
              {
                logger.info(">>>> not checking, internet connections are disabled");
              }
            else
              {
                thread = new Thread()
                  {
                    @Override
                    public void run()
                      {
                        try
                          {
                            updateCache();
                          }
                        catch (Exception e)
                          {
                            logger.warning("While running news feed check: %s", e);
                            logger.throwing(CLASS, "check()", e);
                          }

                        thread = null;
                      }
                  };

                thread.start();
              }
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    public void loadNewsFeed (final @Nonnull NewsUI newsUI)
      {
        logger.info("loadNewsFeed(%s)", newsUI);

        if (rssFeed != null)
          {
            newsUI.notifyFeedAvailable(rssFeed);
          }
        else
          {
            if (status == Status.NEEDS_DOWNLOAD)
              {
                try
                  {
                    probeCache();
                  }
                catch (Exception e)
                  {
                    newsUI.notifyFeedUnavailable(e);
                    return;
                  }
              }

            if (status != Status.NEEDS_DOWNLOAD)
              {
                loadNewsFeedFromCache(newsUI);
              }
            
            // NOT_DOWNLOADED
            else if (networkingPreferences.get().isNetworkConnectionAllowed())
              {
                downloadNewsAndLoadRssFeed(newsUI);
              }
            else
              {
                final String title = NbBundle.getMessage(DefaultNewsController.class, "confirmDownloadTitle");
                final String message = NbBundle.getMessage(DefaultNewsController.class, "confirmDownloadMessage");

                newsUI.getCommonUITasks().alertDialog(title, message, new Runnable()
                  {
                    public void run()
                      {
                        thread1 = new Thread()
                          {
                            @Override
                            public void run()
                              {
                                downloadNewsAndLoadRssFeed(newsUI);
                                thread1 = null;
                              }
                          };

                        thread1.start();
                      }
                  },
                new Runnable()
                  {
                    public void run()
                      {
                        newsUI.close();
                      }
                  });
              }
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void markRead (final @Nonnull Message message)
      throws IOException
      {
        logger.info("markRead(%s)", message);
        readMessages.add(message.getId());
        storeReadMessagesLog();
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void markAllRead (final @Nonnull RSSFeed rssFeed, final @Nonnull NewsUI newsUI)
      throws IOException
      {
        logger.info("markAllRead(%s, %s)", rssFeed, newsUI);

        try
          {
            for (final Message message : rssFeed.get(MESSAGES))
              {
                readMessages.add(message.getId());
              }

            storeReadMessagesLog();
            final String message = NbBundle.getMessage(DefaultNewsController.class, "allMarkedAsRead");
            newsUI.getCommonUITasks().showTemporaryMessage(message);
          }
        catch (NotFoundException e)
          {
            logger.warning("markAllRead(): %s", e);
            logger.throwing(CLASS, "markAllRead()", e);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isRead (final @Nonnull Message message)
      {
        return readMessages.contains(message.getId());
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void downloadNewsAndLoadRssFeed (final @Nonnull NewsUI newsUI)
      {
        logger.fine("downloadNewsAndLoadRssFeed(%s)", newsUI);

        try
          {
            downloadNews();
            loadNewsFeedFromCache(newsUI);
          }
        catch (Exception e)
          {
            newsUI.notifyFeedUnavailable(e);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void loadNewsFeedFromCache (final @Nonnull NewsUI newsUI)
      {
        try
          {
            synchronized (this)
              {
                while (status != Status.DOWNLOADED)
                  {
                    wait();
                  }
              }

            loadFeed();
            newsUI.notifyFeedAvailable(rssFeed);
          }
        catch (Exception e)
          {
            newsUI.notifyFeedUnavailable(e);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void updateCache()
      throws Exception
      {
        logger.info("updateCache()");
        probeCache();

        if (status == Status.NEEDS_DOWNLOAD)
          {
            downloadNews();
            loadFeed();
            checkNotification();
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void loadFeed()
      throws Exception, FileNotFoundException
      {
        InputStream is = null;

        try
          {
            is = new FileInputStream(cachedFile);
            final RSSFeedReader reader = new RSSFeedReader();
            rssFeed = reader.readMessages(is);
          }
        finally
          {
            IoUtils.safeClose(is);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void probeCache()
      throws Exception
      {
        logger.info("probeCache()");
        logger.info(">>>> cached file is %s", cachedFile.getAbsolutePath());

        if (!cachedFile.exists())
          {
            logger.info(">>>> cached file doesn't exist, needs download");
            setStatus(Status.NEEDS_DOWNLOAD);
          }
        else
          {
            final long lastModified = cachedFile.lastModified();
            final long now = getTimestamp();
            final long delta = now - lastModified;

            setStatus((delta >= refreshPeriod) ? Status.NEEDS_DOWNLOAD : Status.DOWNLOADED);

            logger.info(">>>> now: %s, latest news download: %s, delta: %d min, status: %s",
                        new Date(now), new Date(lastModified), delta / ONE_MINUTE, status);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void downloadNews()
      throws FileNotFoundException, IOException, MalformedURLException
      {
        logger.info("downloadNews()");
        setStatus(Status.DOWNLOADING);
        // TODO: be smart, check the last update time of the feed
        final URL url = createUrl(NEWS_FEED_URL);
        final InputStream is = url.openStream();
        final OutputStream os = new FileOutputStream(cachedFile);
        IoUtils.copy(is, os);
        IoUtils.safeClose(is);
        IoUtils.safeClose(os);
        setStatus(Status.DOWNLOADED);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void checkNotification()
      {
        logger.info("checkNotification()");

        try
          {
            final List messageIds = new ArrayList();

            for (final Message message : rssFeed.get(MESSAGES))
              {
                messageIds.add(message.getId());
              }

            messageIds.removeAll(readMessages);

            logger.info(">>>> unread messages: %d", messageIds.size());
            
            if (!messageIds.isEmpty())
              {
                postUnreadNewsNotification();
              }
          }
        catch (NotFoundException e)
          {
            logger.warning("markAllRead(): %s", e);
            logger.throwing(CLASS, "markAllRead()", e);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected void postUnreadNewsNotification()
      {
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected long getTimestamp()
      {
        return System.currentTimeMillis();
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    protected URL createUrl (final String string)
      throws MalformedURLException
      {
        return new URL(string);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected synchronized void setStatus (final @Nonnull Status status)
      {
        logger.info("setStatus(%s)", status);
        this.status = status;
        notifyAll();
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private void loadReadMessagesLog()
      throws IOException
      {
        if (readLogFile.exists())
          {
            readMessages.addAll(new StringsIO(readLogFile).load(new StringsIO.Processor()
              {
                @Nonnull
                public Id processString (final @Nonnull String string)
                  {
                    return new Id(string);
                  }
              }));
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private void storeReadMessagesLog()
      throws IOException
      {
        new StringsIO(readLogFile).store(readMessages);
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy