Package de.nava.informa.utils

Source Code of de.nava.informa.utils.PersistChanGrpMgrTask

// Informa -- RSS Library for Java
//Copyright (c) 2002 by Niko Schmuck
//
//Niko Schmuck
//http://sourceforge.net/projects/informa
//mailto:niko_schmuck@users.sourceforge.net
//
//This library is free software.
//
//You may redistribute it and/or modify it under the terms of the GNU
//Lesser General Public License as published by the Free Software Foundation.
//
//Version 2.1 of the license should be included with this distribution in
//the file LICENSE. If the license is not included with this distribution,
//you may find a copy at the FSF web site at 'www.gnu.org' or 'www.fsf.org',
//or you may write to the Free Software Foundation, 675 Mass Ave, Cambridge,
//MA 02139 USA.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied waranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//$Id: PersistChanGrpMgrTask.java,v 1.19 2006/12/04 23:43:27 italobb Exp $

package de.nava.informa.utils;

import java.net.URL;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.nava.informa.core.ChannelBuilderIF;
import de.nava.informa.core.ChannelFormat;
import de.nava.informa.core.ChannelIF;
import de.nava.informa.core.ItemIF;
import de.nava.informa.impl.hibernate.Channel;
import de.nava.informa.impl.hibernate.ChannelBuilder;
import de.nava.informa.impl.hibernate.Item;
import de.nava.informa.parsers.FeedParser;

/**
* PersistChanGrpMgrTask - description...
*/
public class PersistChanGrpMgrTask extends Thread {

  private static Log logger = LogFactory.getLog(PersistChanGrpMgrTask.class);

  private PersistChanGrpMgr mgr;
  private ChannelBuilder builder;
  private ChannelBuilderIF tempBuilder;
  private Map<URL,UpdateChannelInfo> channelInfos;
  private long minChannelUpdateDelay;
  private volatile boolean running = false;

  /**
   * Construct and setup context of the PersistChanGrpMgr
   *
   * @param mgr
   * @param minChannelUpdateDelay minimum number of millis between channel updates.
   */
  public PersistChanGrpMgrTask(PersistChanGrpMgr mgr, long minChannelUpdateDelay) {
    super("PCGrp: " + mgr.getChannelGroup().getTitle());
    this.minChannelUpdateDelay = minChannelUpdateDelay;
    this.mgr = mgr;
    builder = mgr.getBuilder();
    channelInfos = new HashMap<URL, UpdateChannelInfo>();
    tempBuilder = new de.nava.informa.impl.basic.ChannelBuilder();
  }

  /**
   * Minimum number of milliseconds between updates of channel.
   *
   * @param minChannelUpdateDelay minimum pause between updates in milliseconds.
   */
  public void setMinChannelUpdateDelay(long minChannelUpdateDelay) {
    this.minChannelUpdateDelay = minChannelUpdateDelay;
  }

  /**
   * run - Called each iteration to process all the Channels in this Group. This will skip inactive
   * channels. -
   */
  public void run() {
    running = true;

    try {
      // We do job and sleep until someone interupts us.
      while (!isInterrupted()) {
        long startedLoop = System.currentTimeMillis();

        performUpdates();

        // Calculate time left to sleep beween updates
        long leftToSleep = minChannelUpdateDelay - (startedLoop - System.currentTimeMillis());
        logger.debug("Going to sleep for " + leftToSleep + " millis");
        if (leftToSleep > 0) Thread.sleep(leftToSleep);
      }
    } catch (InterruptedException e) {
      // Note that the catch looks like it just continues, but at the same time isInterrupted() goes
      // to true and ends the
      logger.warn("Interrupted exception within Run method");
    } catch (Exception ignoredException) // Ignore all Exceptions (assuming that they did their own
    // cleanup and we want to keep on polling.
    {
      ignoredException.printStackTrace();
      // and continue

    } finally {
      running = false;
      synchronized (this) {
        notifyAll();
      }
    }
  }

  /**
   * Returns TRUE if current thread is running.
   *
   * @return TRUE if running.
   */
  public boolean isRunning() {
    return running;
  }

  /**
   * Interrupt the thread and return.
   *
   * @see java.lang.Thread#interrupt()
   */
  public void interrupt() {
    interrupt(false);
  }

  /**
   * Interrupts execution of task.
   *
   * @param wait TRUE to wait for finish of task.
   */
  public void interrupt(boolean wait) {
    super.interrupt();
    if (wait && isRunning()) {
      while (isRunning()) {
        try {
          synchronized (this) {
            wait(1000);
          }
        } catch (InterruptedException e) {
        }
      }
    }
  }

  /**
   * Perform single update cycle for current group.
   */
  public void performUpdates() {
    logger.debug("Starting channel updates loop for " + mgr.getChannelGroup().getTitle());
    mgr.notifyPolling(true);
    Iterator iter = mgr.channelIterator();

    Channel nextChan;
    while (iter.hasNext()) {
      nextChan = (Channel) iter.next();
      logger.info("processing: " + nextChan);
     
// Catch all Exceptions coming out of handleChannel and continue iterating to the next one.
     
      try {
        handleChannel(nextChan, getUpdChanInfo(nextChan));
      } catch (RuntimeException e) {
        logger.error("Error during processing: " + nextChan, e);
      } catch (NoSuchMethodError ignoreNoSuchMethod) // Ignore and continue
      {
        logger
            .error("NoSuchMethodError exception within Run method. Ignoring." + nextChan, ignoreNoSuchMethod);
      }

    }

    // Notify everyone that polling of group finished.
    mgr.notifyPolling(false);
    mgr.incrPollingCounter();
  }

  /**
   * Return (and create if necessary) an UpdateChannelInfo object, which is a parallel object which
   * we use here to keep track of information about a channel.
   *
   * @param chan - Corresponding Channel.
   */
  private UpdateChannelInfo getUpdChanInfo(Channel chan) {
    UpdateChannelInfo info = channelInfos.get(chan.getLocation());

    if (info == null) // Create a new UpdateChannelInfo object and add it to the Map.
    {
      info = new UpdateChannelInfo(mgr.getAcceptNrErrors());
      channelInfos.put(chan.getLocation(), info);
    }
    return info;
  }

  /**
   * Process the Channel information.
   *
   * @param chan - Channel to process
   * @param info - UpdateChannelInfo - additional Channel Info object
   */
  private void handleChannel(Channel chan, UpdateChannelInfo info) {
    if (!info.shouldDeactivate()) {
      if (shouldUpdate(info)) {
        synchronized (builder) {
          if (!info.getFormatDetected()) handleChannelHeader(chan, info);
          handleChannelItems(chan, info);
        }

        info.setLastUpdatedTimestamp(System.currentTimeMillis());
      }
    } else {
      // Returns true if more errors happened than threshold.
      logger.info("Not processing channel: " + chan + " because exceeded error threshold.");
      return;
    }
  }

  /**
   * Returns TRUE if the cannel represented by the <code>info</code> should be updated. Decision
   * is basing on the fact of last update. If there's not enough time passed since then we don't
   * need to update this channel.
   *
   * @param info info object of the channel.
   *
   * @return result of the check.
   */
  private boolean shouldUpdate(UpdateChannelInfo info) {
    return System.currentTimeMillis() - info.getLastUpdatedTimestamp() > minChannelUpdateDelay;
  }

  /**
   * handleChannelHeader -
   *
   * @param chan
   * @param info -
   */
  private void handleChannelHeader(Channel chan, UpdateChannelInfo info) {
    if (!info.getFormatDetected()) { // If format has been detected then we've seen this Channel
      // already
      logger.debug("Handling Channel Header. Format not yet detected.");
      try {
        builder.beginTransaction();
        builder.reload(chan);
        ChannelFormat format = FormatDetector.getFormat(chan.getLocation());
        chan.setFormat(format);
        info.setFormatDetected(true);
        chan.setLastUpdated(new Date());
        builder.endTransaction();
      } catch (UnknownHostException e) {
        // Normal situation when user is offline
        logger.debug("Host not found: " + e.getMessage());
      } catch (Exception e) {
        info.increaseProblemsOccurred(e);
        String msg = "Exception in handleChannelHeader for : " + chan;
        logger.fatal(msg + "\n     Continue....");
      } finally {
        // If there was an exception we still will be in transaction.
        if (builder.inTransaction()) builder.resetTransaction();
      }
    }
  }

  /**
   * Process items in the newly parsed Channel. If they are new (i.e. not yet persisted) then add
   * them to the Channel. Note the logXXX variables were put in to do better error reporting in the
   * event of an Exception.
   *
   * @param chan
   * @param info -
   */
  private void handleChannelItems(Channel chan, UpdateChannelInfo info) {
    ChannelIF tempChannel = null;
    int logHowManySearched = 0;
    int logHowManyAdded = 0;

    // TODO: [Aleksey Gureev] I don't see locking of builder here. Locking of the whole peice will
    // be very
    // great resource consumption. It's necessary to rework whole method to lock builder for a
    // minimal time.

    try {
      builder.beginTransaction();
      builder.reload(chan);
      /*
       * We will now parse the new channel's information into a *memory based* temporary channel. We
       * will then see which items that we received from the feed are already present and add the
       * new ones.
       */
      tempChannel = FeedParser.parse(tempBuilder, chan.getLocation());
      InformaUtils.copyChannelProperties(tempChannel, chan);
      /*
       * Tricky: this channel might have been loaded into memory by Hibernate in a preceding
       * Hibernate Session. We need to make it available in this session so it will be written back
       * to disk when the transaction is committed.
       */
      chan.setLastUpdated(new Date());
      mgr.notifyChannelRetrieved(chan);
      /*
       * Compare with the existing items, and only add new ones. In the future this is where we
       * would put code to diff an item to see how blog author has edited a certain item over time.
       */
      if (!tempChannel.getItems().isEmpty()) {
        Iterator it = tempChannel.getItems().iterator();
        while (it.hasNext()) {
          logHowManySearched++;
          de.nava.informa.impl.basic.Item transientItem = (de.nava.informa.impl.basic.Item) it
              .next();
          if (!chan.getItems().contains(transientItem)) {
            logger.info("Found new item: " + transientItem);
            logHowManyAdded++;
            /*
             * A persistent item is created, using all the state from the memory based item produced
             * by parser.
             */
            ItemIF newItem = builder.createItem(chan, transientItem);
            mgr.notifyItemAdded((Item) newItem);
          }
        } // while it.hasNext()
      }
      builder.endTransaction();
    } catch (UnknownHostException e) {
      // Normal situation when user is offline
      logger.debug("Host not found: " + e.getMessage());
    } catch (Exception e) {
      info.increaseProblemsOccurred(e);
      String msg = "Exception in handleChannelItems. # Potential new items = " + logHowManySearched
          + ", # Items actually added to channel: " + logHowManyAdded + "\n     Stored Chan="
          + chan + "\n     ParsedChan=" + tempChannel;
      logger.fatal(msg + "\n     Continue....");
    } finally {
      // If there was an exception we still will be in transaction.
      if (builder.inTransaction()) builder.resetTransaction();
    }
  }
}
TOP

Related Classes of de.nava.informa.utils.PersistChanGrpMgrTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.