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

org.ict4h.atomfeed.client.service.AtomFeedClient Maven / Gradle / Ivy

The newest version!
package org.ict4h.atomfeed.client.service;

import com.sun.syndication.feed.atom.Entry;
import com.sun.syndication.feed.atom.Feed;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ict4h.atomfeed.client.domain.Event;
import org.ict4h.atomfeed.client.domain.FailedEvent;
import org.ict4h.atomfeed.client.domain.FailedEventRetryLog;
import org.ict4h.atomfeed.client.domain.Marker;
import org.ict4h.atomfeed.client.exceptions.AtomFeedClientException;
import org.ict4h.atomfeed.client.AtomFeedProperties;
import org.ict4h.atomfeed.client.repository.AllFailedEvents;
import org.ict4h.atomfeed.client.repository.AllFeeds;
import org.ict4h.atomfeed.client.repository.AllMarkers;
import org.ict4h.atomfeed.client.util.Util;
import org.ict4h.atomfeed.transaction.AFTransactionManager;
import org.ict4h.atomfeed.transaction.AFTransactionWork;
import org.ict4h.atomfeed.transaction.AFTransactionWorkWithoutResult;

import java.net.URI;
import java.util.Date;
import java.util.List;

public class AtomFeedClient implements FeedClient {
    private static Logger logger = LogManager.getLogger(AtomFeedClient.class);

    private AllFeeds allFeeds;
    private AtomFeedProperties atomFeedProperties;

    private AFTransactionManager transactionManager;
    private URI feedUri;
    private EventWorker eventWorker;
    private AllMarkers allMarkers;
    private AllFailedEvents allFailedEvents;

    AtomFeedClient(AllFeeds allFeeds, AllMarkers allMarkers, AllFailedEvents allFailedEvents, URI feedUri, EventWorker eventWorker) {
        this(allFeeds, allMarkers, allFailedEvents, new AtomFeedProperties(), null, feedUri, eventWorker);
    }

    public AtomFeedClient(AllFeeds allFeeds, AllMarkers allMarkers, AllFailedEvents allFailedEvents, AtomFeedProperties atomFeedProperties,
                          AFTransactionManager transactionManager,
                          URI feedUri, EventWorker eventWorker) {
        this.allFeeds = allFeeds;
        this.allMarkers = allMarkers;
        this.allFailedEvents = allFailedEvents;
        this.atomFeedProperties = atomFeedProperties;
        this.transactionManager = transactionManager;
        this.feedUri = feedUri;
        this.eventWorker = eventWorker;
    }

    @Override
    public void processEvents() {
        logger.info(String.format("Processing events for feed URI : %s using event worker : %s", this.feedUri, eventWorker.getClass().getSimpleName()));
        try {
            Marker lastRead = transactionManager.executeWithTransaction(new MarkerReader(feedUri));
            final FeedEnumerator enumerator = new FeedEnumerator(allFeeds, lastRead);
            for (final Entry entry : enumerator) {
                Integer numberOfFailedEvents = transactionManager.executeWithTransaction(new FailedEventCounter(feedUri));
                if ((numberOfFailedEvents.intValue() >= atomFeedProperties.getMaxFailedEvents())) {
                    logger.error(String.format("Too many failed events for URI:%s have failed while processing. Cannot continue.", feedUri));
                    return;
                }
                Event eventInProcess = null;
                try {
                    eventInProcess = new Event(entry, getEntryFeedUri(enumerator));
                    logger.info(String.format("Processing event : %s", eventInProcess));
                    transactionManager.executeWithTransaction(new EventProcessor(eventInProcess, enumerator.getCurrentFeed()));
                } catch (final Exception eventProcessingException) {
                    logger.error(String.format("Error occurred while processing feed entry:%s", entry), eventProcessingException);
                    try {
                        transactionManager.executeWithTransaction(new FailedEventHandler(feedUri, entry, eventInProcess, enumerator.getCurrentFeed(), eventProcessingException));
                    } catch (Exception feEx) {
                        String errorMsg = String.format("Error occurred while trying to save event as Failed: %s", eventInProcess);
                        logger.error(errorMsg, feEx);
                        throw new RuntimeException(errorMsg, feEx);
                    }
                } finally {
                    eventWorker.cleanUp(eventInProcess);
                }
            }
        } catch (Exception e) {
            throw new AtomFeedClientException(e);
        } finally {
            //?
        }
    }

    @Override
    public void processFailedEvents() {
        logger.info(String.format("Processing failed events for feed URI : %s using event worker : %s",
                feedUri, eventWorker.getClass().getSimpleName()));
        try {
            List failedEvents = transactionManager.executeWithTransaction(new FailedEventsFetcher());
            for (final FailedEvent failedEvent : failedEvents) {
                try {
                    logger.info(String.format("Processing previously failed event : %s", failedEvent));
                    transactionManager.executeWithTransaction(new FailedEventProcessor(failedEvent));
                } catch (final Exception retryException) {
                    logger.error(String.format("Failed to process failed event. %s", failedEvent), retryException);
                    try {
                        transactionManager.executeWithTransaction(new EventRetryFailureHandler(failedEvent, retryException));
                    } catch (Exception fePEx) {
                        String errorMsg = String.format("Error occurred while trying to update failed event. %s", failedEvent);
                        logger.error(errorMsg, fePEx);
                        throw new RuntimeException(errorMsg,fePEx);
                    }
                }
            }
        } catch (Exception e) {
            throw new AtomFeedClientException(e);
        } finally {
            //?
        }

    }


    private void updateFailedEvents(FailedEvent failedEvent, Exception e)  {
        failedEvent.incrementRetryCount();
        allFailedEvents.addOrUpdate(failedEvent);

        long failedAt = new Date().getTime();
        String exceptionString = Util.getExceptionString(e);
        String eventContent = failedEvent.getEvent().getContent();
        FailedEventRetryLog failedEventRetryLog = new FailedEventRetryLog(failedEvent.getFeedUri(), failedAt, exceptionString, failedEvent.getEventId(), eventContent);
        allFailedEvents.insert(failedEventRetryLog);
    }

    private String getEntryFeedUri(FeedEnumerator feedEnumerator) {
        return Util.getSelfLink(feedEnumerator.getCurrentFeed()).toString();
    }

    private void handleFailedEvent(Entry entry, URI feedUri, Exception e, Feed feed, Event event) {
        final URI viaLink = Util.getViaLink(feed);
        final String errorMessage = String.format("Failed processing event in feed [%s] \n", viaLink.toString()).concat(Util.getExceptionString(e));
        allFailedEvents.addOrUpdate(new FailedEvent(feedUri.toString(), event, errorMessage, 0));
        if (atomFeedProperties.controlsEventProcessing()) {
            allMarkers.put(this.feedUri, entry.getId(), viaLink);
        }
    }

    private class FailedEventCounter implements AFTransactionWork {
        private URI feedURI;
        public FailedEventCounter(URI feedURI) {
            this.feedURI = feedURI;
        }
        @Override
        public Integer execute() {
            return allFailedEvents.getNumberOfFailedEvents(this.feedURI.toString());
        }
        @Override
        public PropagationDefinition getTxPropagationDefinition() {
            return PropagationDefinition.PROPAGATION_REQUIRED;
        }
    }

    private class EventProcessor extends AFTransactionWorkWithoutResult {
        private Event eventInProcess;
        private Feed currentFeed;
        public EventProcessor(Event eventInProcess, Feed currentFeed) {
            this.eventInProcess = eventInProcess;
            this.currentFeed = currentFeed;
        }
        @Override
        protected void doInTransaction() {
            logger.debug("Processing event : " + this.eventInProcess);
            eventWorker.process(this.eventInProcess);
            if (atomFeedProperties.controlsEventProcessing()) {
                allMarkers.put(feedUri, this.eventInProcess.getId(), Util.getViaLink(this.currentFeed));
            }
        }
        @Override
        public AFTransactionWork.PropagationDefinition getTxPropagationDefinition() {
            return AFTransactionWork.PropagationDefinition.PROPAGATION_REQUIRES_NEW;
        }
    }

    public class MarkerReader implements AFTransactionWork {
        private URI feedUri;
        public MarkerReader(URI feedUri) {
            this.feedUri = feedUri;
        }
        @Override
        public Marker execute() {
            Marker lastRead = allMarkers.get(feedUri);
            if (lastRead == null) {
                lastRead = new Marker(feedUri, null, null);
            }
            return lastRead;
        }
        @Override
        public PropagationDefinition getTxPropagationDefinition() {
            return PropagationDefinition.PROPAGATION_REQUIRED;
        }
    }

    class FailedEventHandler extends AFTransactionWorkWithoutResult {
        private URI feedURI;
        private Entry failedEntry;
        private Event failedEvent;
        private Feed workingFeed;
        private Exception failureException;

        public FailedEventHandler(URI feedURI, Entry failedEntry, Event failedEvent, Feed workingFeed, Exception failureException) {
            this.feedURI = feedURI;
            this.failedEntry = failedEntry;
            this.failedEvent = failedEvent;
            this.workingFeed = workingFeed;
            this.failureException = failureException;
        }
        @Override
        protected void doInTransaction() {
            handleFailedEvent(this.failedEntry, this.feedURI, this.failureException, this.workingFeed, this.failedEvent);
        }
        @Override
        public PropagationDefinition getTxPropagationDefinition() {
            return PropagationDefinition.PROPAGATION_REQUIRES_NEW;
        }
    }

    class EventRetryFailureHandler extends AFTransactionWorkWithoutResult {
        private FailedEvent failedEvent;
        private Exception failureException;
        public EventRetryFailureHandler(FailedEvent failedEvent, Exception failureException) {
            this.failedEvent = failedEvent;
            this.failureException = failureException;
        }
        @Override
        public AFTransactionWork.PropagationDefinition getTxPropagationDefinition() {
            return AFTransactionWork.PropagationDefinition.PROPAGATION_REQUIRES_NEW;
        }
        @Override
        protected void doInTransaction() {
            updateFailedEvents(this.failedEvent, this.failureException);
        }
    }

    class FailedEventProcessor extends AFTransactionWorkWithoutResult {
        private final FailedEvent eventInProcess;
        public FailedEventProcessor(FailedEvent failedEvent) {
            this.eventInProcess = failedEvent;
        }
        @Override
        public PropagationDefinition getTxPropagationDefinition() {
            return PropagationDefinition.PROPAGATION_REQUIRES_NEW;
        }
        @Override
        protected void doInTransaction() {
            logger.debug(String.format("Processing failed event : %s", eventInProcess));
            eventWorker.process(eventInProcess.getEvent());
            allFailedEvents.remove(eventInProcess);
        }
    }

    class FailedEventsFetcher implements AFTransactionWork> {
        @Override
        public List execute() {
            return allFailedEvents.getOldestNFailedEvents(feedUri.toString(), getProcessBatchSizeForFailedEvents(), atomFeedProperties.getFailedEventMaxRetry());
        }
        @Override
        public PropagationDefinition getTxPropagationDefinition() {
            return PropagationDefinition.PROPAGATION_REQUIRED;
        }
    }

    private int getProcessBatchSizeForFailedEvents() {
        return atomFeedProperties.getFailedEventsBatchProcessSize();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy