Package com.collective2.signalEntry.implementation

Source Code of com.collective2.signalEntry.implementation.ResponseManager

/**
* This notice shall not be removed.
* See the "LICENSE.txt" file found in the root folder
* for the full license governing this code.
* Nathan Tippy  7/22/12
*/
package com.collective2.signalEntry.implementation;

import com.collective2.signalEntry.C2ServiceException;
import com.collective2.signalEntry.Parameter;
import com.collective2.signalEntry.Response;
import com.collective2.signalEntry.adapter.C2EntryServiceAdapter;
import com.collective2.signalEntry.adapter.IterableXMLEventReader;
import com.collective2.signalEntry.approval.C2EntryHumanApproval;
import com.collective2.signalEntry.journal.C2EntryServiceJournal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.concurrent.*;

public class ResponseManager {
    private static final Logger         logger = LoggerFactory.getLogger(ResponseManager.class);

    private final C2EntryServiceAdapter adapter;
    private final C2EntryServiceJournal journal;
    private final long                  networkDownRetryDelay;
    private final boolean               showDeepStacks = true;
    private final C2EntryHumanApproval  approvalRequestable;
    private boolean                     isClean;//is false when out of sync with journal
    private C2ServiceException          haltingException;


    private static final String        threadName = "C2EntryServiceResponseManager";
    private static final ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setDaemon(true);
            thread.setName(threadName);
            thread.setPriority(Thread.MAX_PRIORITY);
            return thread;
        }
    };

    //Must be singleThreadExecutor only because each callable must be called sequentially
    private final static ExecutorService    executor = Executors.newSingleThreadExecutor(threadFactory);
    private final static Runnable           placeHolder = new Runnable() {
        @Override
        public void run() {
        }
    };

    public ResponseManager(C2EntryServiceAdapter    adapter,
                           C2EntryServiceJournal    journal,
                           C2EntryHumanApproval     approvalRequestable,
                           long                     networkDownRetryDelay
                           ) {
        this.adapter                = adapter;
        this.journal                = journal;
        this.approvalRequestable    = approvalRequestable;
        this.networkDownRetryDelay  = networkDownRetryDelay;
    }

    public Exception getHaltingException(){
        return haltingException;
    }

    public void awaitPending(long seconds) throws TimeoutException {
        try {
            //if executor has gotten down to this one then everything else is done
            executor.submit(placeHolder).get(seconds,TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            throw new C2ServiceException("awaitPending",e.getCause(),false);
        }
    }

    public void awaitPending() {
        try {
            //if executor has gotten down to this one then everything else is done
            executor.submit(placeHolder).get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            throw new C2ServiceException("awaitPending",e.getCause(),false);
        }
    }

    public Request[] dropPending() {
        return journal.dropPending();
    }

    public synchronized void reloadPendingRequests(String password) {
        if (!isClean) {
            //reload old pending requests
            Iterator<Request> iterator = this.journal.pending();
            if (iterator.hasNext()) {
                //never modify object passed in or this may leak the password out!
                Request journalInstance = iterator.next();
                Request request = journalInstance.secureClone();
                if (request.containsKey(Parameter.Password)) {
                    request.put(Parameter.Password, password);
                }
                Response r = new ImplResponse(this, request, executor.submit(callable(request, journalInstance)));
                 //Note: may want to add a recovery listener here
            }
            while(iterator.hasNext()) {
                //never modify object passed in or this may leak the password out!
                Request journalInstance = iterator.next();
                Request request = journalInstance.secureClone();
                if (request.containsKey(Parameter.Password)) {
                    request.put(Parameter.Password, password);
                }
                Response r = new ImplResponse(this, request, executor.submit(callable(request, journalInstance)));
                 //Note: may want to add a recovery listener here
            }
            isClean=true;
        }
    }

    public Response fetchResponse(Request request) {
        assert(request.validate());

        synchronized (this) {
            Request journalInstance = request.secureClone();//use the same instance for all journal work
            journal.append(journalInstance); //must add to journal before submit to executor
            if (!journalInstance.isApprovalKnown() && journalInstance.command().approvalRequired()) {
                approvalRequestable.oneMoreRequest(journalInstance);
            }
            return new ImplResponse(this, request, executor.submit(callable(request,journalInstance)));
        }
    }

    private Callable<IterableXMLEventReader> callable(final Request request, final Request journalInstance) {

        return new Callable<IterableXMLEventReader>() {

            C2ServiceException optionalStackTrace = showDeepStacks ?
                                                    new C2ServiceException("Originating Call Stack",false) :
                                                    null;

            @Override
            public IterableXMLEventReader call() throws Exception {

                try {
                    if (!journalInstance.isApprovalKnown() && journalInstance.command().approvalRequired()) {
                        //must get approval for these!
                        assert(journal.pending().next().equals(journalInstance));
                        approvalRequestable.waitForApproval(journal.pending());
                    }

                    if (!journalInstance.isApproved()) {
                       journal.markRejected(journalInstance);
                       return new IterableXMLEventReader("<rejected>not approved</rejected>");
                    };

                    //was validated upon construction but assert it was not changed in the meantime
                    assert(request.validate());
                    //all down stream requests must see the same halting exception until its reset.
                    if (haltingException != null ) {
                        throw haltingException;
                    }

                    boolean  tryAgain = false;
                    do {
                        try {
                            //exceptions thrown here are because
                            // * the network is down and we should try later
                            // * the response was not readable - must stop all

                            //transmit to the adapter
                            IterableXMLEventReader eventReader = adapter.transmit(request);
                            synchronized (ResponseManager.this) {
                                //exceptions thrown here are because
                                // * database was unable to change flag on request to sent - must stop all
                                journal.markSent(journalInstance);
                            }
                            return eventReader;

                        } catch (C2ServiceException e) {
                            tryAgain = e.tryAgain();//if true wait for configured delay and try again.
                            if (tryAgain) {

                                if (null != request.get(Parameter.CancelsAtRelative)) {
                                    //cant try again or delay because the request is time dependent relative to submission time
                                    haltingException = new C2ServiceException("Can not retry request which makes use of CancelsAtRelative feature",false);
                                    throw haltingException;
                                }

                                try {
                                    Thread.sleep(networkDownRetryDelay);
                                } catch (InterruptedException ie) {
                                    throw e; //this is not a halting exception
                                }
                            } else {
                                haltingException = e;
                                throw haltingException;
                            }
                        } catch (Exception ex) {
                            haltingException = new C2ServiceException(request.toString(),ex,false);
                            throw haltingException;
                        }
                    } while (tryAgain);
                    throw new C2ServiceException("Unable to execute.",true);

                } catch (RuntimeException e) {
                    if (optionalStackTrace!=null) {
                        optionalStackTrace.overrideCause(e);
                        throw optionalStackTrace;
                    } else {
                        throw e;
                    }
                }
            }

        };
    }


}
TOP

Related Classes of com.collective2.signalEntry.implementation.ResponseManager

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.