Changeset 5762


Ignore:
Timestamp:
Nov 28, 2019, 1:52:09 PM (3 years ago)
Author:
Nicklas Nordborg
Message:

Fixes #1207: Replace the "Nothing yet" in the activity log with a quote-of-the-day

Quotes can now be downloaded from https://quotes.rest/qod.json

Location:
extensions/net.sf.basedb.reggie/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • extensions/net.sf.basedb.reggie/trunk/config/reggie-config.xml

    r5737 r5762  
    2323    <!-- Max age (in days) of entries to display (even if the max number hasn't been reached) -->
    2424    <max-age-in-days>14</max-age-in-days>
     25    <quote-of-the-day>
     26      <!-- URL to quote-of-the-day endpoint (optional, set an empty URL to disable this feature) -->
     27      <url>https://quotes.rest/qod.json</url>
     28      <!-- Default is 12 hours; do not set to less than 3600 since the external API has a limit -->
     29      <max-age-in-seconds>43200</max-age-in-seconds>
     30    </quote-of-the-day>
    2531  </activity-log>
    2632
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/activity/ActivityLog.java

    r5761 r5762  
    11package net.sf.basedb.reggie.activity;
    22
     3import java.io.InputStream;
     4import java.io.InputStreamReader;
     5import java.io.StringWriter;
     6import java.net.URL;
     7import java.net.URLConnection;
    38import java.nio.charset.Charset;
     9import java.nio.charset.StandardCharsets;
    410import java.text.SimpleDateFormat;
    511import java.time.LocalDateTime;
     
    2127import org.jdom2.output.Format;
    2228import org.jdom2.output.XMLOutputter;
     29import org.json.simple.JSONArray;
     30import org.json.simple.JSONObject;
     31import org.json.simple.parser.JSONParser;
    2332import org.slf4j.Logger;
    2433import org.slf4j.LoggerFactory;
    2534
    2635import net.sf.basedb.clients.web.extensions.ExtensionsControl;
     36import net.sf.basedb.core.AbsoluteProgressReporter;
    2737import net.sf.basedb.core.Application;
    2838import net.sf.basedb.reggie.Reggie;
    2939import net.sf.basedb.reggie.XmlConfig;
     40import net.sf.basedb.util.FileUtil;
    3041import net.sf.basedb.util.Values;
    3142
     
    106117  private int maxAgeInDays = 14;
    107118  private int maxEntries = 35;
     119  private int maxQuoteAgeInSeconds = 3600 * 12; //  12 hours is the default
     120  private URL quoteUrl;
    108121 
    109122  private volatile byte[] rssFeed;
     
    187200    maxAgeInDays = Values.getInt(cfg.getConfig("activity-log/max-age-in-days"), 14);
    188201    maxEntries = Values.getInt(cfg.getConfig("activity-log/max-entries"), 35);
     202    maxQuoteAgeInSeconds = Values.getInt(cfg.getConfig("activity-log/quote-of-the-day/max-age-in-seconds"), 12*3600);
     203    try
     204    {
     205      quoteUrl = new URL(cfg.getConfig("activity-log/quote-of-the-day/url"));
     206    }
     207    catch (Exception ex)
     208    {
     209      quoteUrl = null;
     210    }
    189211  }
    190212
    191213  public QuoteOfTheDay getQuoteOfTheDay()
    192214  {
    193     if (quoteOfTheDay == null)
    194     {
    195       QuoteOfTheDay tmp = new QuoteOfTheDay();
    196       tmp.setAuthor("Indiana Jones - Raiders of the Lost Ark");
    197       tmp.setQuote("There's a big snake in the plane, Jock!");
    198       quoteOfTheDay = tmp;
    199     }
    200     return quoteOfTheDay;
     215    if (quoteUrl == null) return null; // Disabled
     216   
     217    if (quoteOfTheDay == null || quoteOfTheDay.getAgeInSeconds() > maxQuoteAgeInSeconds)
     218    {
     219      synchronized (quoteUrl)
     220      {
     221        if (quoteOfTheDay == null || quoteOfTheDay.getAgeInSeconds() > maxQuoteAgeInSeconds)
     222        {
     223          InputStream in = null;
     224          QuoteOfTheDay tmp = new QuoteOfTheDay();
     225          try
     226          {
     227            // Read data from the URL, if external we have a fixed limit
     228            AbsoluteProgressReporter maxDataFilter = quoteUrl.getProtocol().startsWith("http") ? new MaxDownloadFilter(5000) : null;
     229            logger.debug("Loading quote from " + quoteUrl);
     230            URLConnection conn = quoteUrl.openConnection();
     231            conn.setConnectTimeout(3000);
     232            conn.setReadTimeout(3000);
     233            in = conn.getInputStream();
     234            StringWriter sout = new StringWriter();
     235            FileUtil.copy(new InputStreamReader(in, StandardCharsets.UTF_8), sout, maxDataFilter);
     236            logger.debug("A quote was loaded from " + quoteUrl + "; bytes="+sout.getBuffer().length());
     237           
     238            // Parse as JSON { "contents": { "quotes": [ {"quote": "...", "author": "..." }, {...} ] }}
     239            JSONObject json = (JSONObject)new JSONParser().parse(sout.toString());
     240            json = (JSONObject)json.get("contents");
     241            JSONArray jsonQuotes = (JSONArray)json.get("quotes");
     242           
     243            int index = (int)Math.floor(Math.random()*jsonQuotes.size());
     244            JSONObject jsonQuote = (JSONObject)jsonQuotes.get(index);       
     245            String quote = (String)jsonQuote.get("quote");
     246            String author = (String)jsonQuote.get("author");
     247           
     248            // Check that we have values of acceptable lengths           
     249            if (quote != null && author != null && quote.length() < 160 && author.length() < 40)
     250            {
     251              tmp.setAuthor(author);
     252              tmp.setQuote(quote);
     253              logger.debug("A quote was successfully loaded from "+ quoteUrl);
     254            }
     255          }
     256          catch (Exception ex)
     257          {
     258            logger.warn("Could not load quote-of-the-day", ex);
     259          }
     260          finally
     261          {
     262            FileUtil.close(in);
     263            quoteOfTheDay = tmp;
     264          }
     265        }
     266      }
     267    }
     268    else if (logger.isDebugEnabled() || true)
     269    {
     270      logger.debug("Using existing quote; age="+quoteOfTheDay.getAgeInSeconds());
     271    }
     272    return quoteOfTheDay == null || quoteOfTheDay.getQuote() == null ? null : quoteOfTheDay;
    201273  }
    202274 
     
    441513  }
    442514
     515  /**
     516    To prevent external data overflow.
     517  */
     518  static class MaxDownloadFilter
     519    implements AbsoluteProgressReporter
     520  {
     521 
     522    private final long maxBytes;
     523   
     524    public MaxDownloadFilter(long maxBytes)
     525    {
     526      this.maxBytes = maxBytes;
     527    }
     528   
     529    @Override
     530    public void display(int percent, String message)
     531    {}
     532 
     533    @Override
     534    public void append(String message)
     535    {}
     536 
     537    @Override
     538    public void displayAbsolute(long completed, String message)
     539    {
     540      if (completed > maxBytes)
     541      {
     542        throw new RuntimeException("Too much data: " + completed + "; max allowed: " + maxBytes);
     543      }
     544    }
     545 
     546  }
    443547}
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/activity/QuoteOfTheDay.java

    r5761 r5762  
    77public class QuoteOfTheDay
    88{
     9  private final long created;
    910  private String quote;
    1011  private String author;
    1112 
    1213  public QuoteOfTheDay()
    13   {}
     14  {
     15    this.created = System.currentTimeMillis();
     16  }
    1417 
    1518  /**
     
    3942  }
    4043 
     44  /**
     45    Get the age of the quote in seconds.
     46  */
     47  public int getAgeInSeconds()
     48  {
     49    return (int)((System.currentTimeMillis() - created) / 1000);
     50  }
     51 
    4152}
Note: See TracChangeset for help on using the changeset viewer.