Package org.dmd.dmp.server.servlet.base.plugins

Source Code of org.dmd.dmp.server.servlet.base.plugins.BasicRequestTrackerPlugin$RequestInfo

//  ---------------------------------------------------------------------------
//  dark-matter-data
//  Copyright (c) 2012 dark-matter-data committers
//  ---------------------------------------------------------------------------
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU Lesser General Public License as published by the
//  Free Software Foundation; either version 3 of the License, or (at your
//  option) any later version.
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
//  more details.
//  You should have received a copy of the GNU Lesser General Public License along
//  with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
//  ---------------------------------------------------------------------------
package org.dmd.dmp.server.servlet.base.plugins;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.dmd.dmp.server.extended.Request;
import org.dmd.dmp.server.extended.Response;
import org.dmd.dmp.server.servlet.base.DmpServletPlugin;
import org.dmd.dmp.server.servlet.base.interfaces.DmpRequestProcessorIF;
import org.dmd.dmp.server.servlet.base.interfaces.DmpResponseHandlerIF;
import org.dmd.dmp.server.servlet.base.interfaces.RequestTrackerIF;

/**
* The RequestTrackerPlugin implements the RequestTrackerIF interface and provides a
* standard mechanism of handling both synchronous and asynchronous Dark Matter Protocol
* Requests.
*/
public class BasicRequestTrackerPlugin extends DmpServletPlugin implements RequestTrackerIF
{
  // The synch response handler is used to provide synchronized handling of responses
  // from a DmpRequestProcessorIF.
    private SynchResponseHandler                                                synchResponseHandler = new SynchResponseHandler();

    private HashMap<Class<? extends Request>, LinkedList<DmpRequestProcessorIF>>   requestProcessors    = new HashMap<Class<? extends Request>, LinkedList<DmpRequestProcessorIF>>();
    private DmpRequestProcessorIF                                                  defaultRequestHandler;
    private int                                                                 nextRequestId        = 1;
    private HashMap<Integer, RequestInfo>                                       outstandingRequests  = new HashMap<Integer, RequestInfo>();
    private HashMap<Integer, Response>                                          pendingResponses     = new HashMap<Integer, Response>();
    private Timer                                                               timer                = new Timer();

    private Logger                                   logger         = LoggerFactory.getLogger(getClass());
   
    /**
     * The RequestTracker uses RequestInfo to allow it to map responses back to the appropriate
     * response handler. There is a bit of trickery involved here in order to handle the fact
     * that HttpServletRequests i.e. Dark Matter Requests that arrive via GWT RPC must be
     * handled synchronously. When a RequestInfo is created, it has two response handlers.
     * The firstResponseHandler is set to be the SynchResponseHandler in cases where we're
     * handling DMP request from GWT RPC. Once the first response is sent back, we fall back
     * to use the asyncResponseHandler, which will send any further responses back on the
     * GWT event channel. In this way, we achieve true asynchronous behaviour if we require it.
     *
     * For normal asynchronous requests, the first and async handlers are actually the same handler.
     */
    private class RequestInfo
    {
        Request                req;
        DmpResponseHandlerIF   firstResponseHandler;
        DmpResponseHandlerIF   asyncResponseHandler;
        DmpResponseHandlerIF   responseHandler;
        long                   timeoutTime;

        RequestInfo(Request req, DmpResponseHandlerIF firstResponseHandler, DmpResponseHandlerIF asyncResponseHandler, long timeoutTime)
        {
            this.req          = req;
            this.firstResponseHandler   = firstResponseHandler;
            this.asyncResponseHandler  = asyncResponseHandler;
            responseHandler       = firstResponseHandler;
            this.timeoutTime       = timeoutTime;
        }
    }
   
    /**
     * This task is called every second to check for timed out requests.
     */
    private class RequestTimeoutProcessor extends TimerTask
    {
        public void run()
        {
            synchronized (outstandingRequests)
            {
                long currentTime = System.currentTimeMillis();
                Collection<Entry<Integer, RequestInfo>> entries = new LinkedList<Entry<Integer, RequestInfo>>(outstandingRequests.entrySet());
                for (Entry<Integer, RequestInfo> entry : entries)
                {
                    RequestInfo ri = entry.getValue();
                    if (currentTime < ri.timeoutTime)
                      continue;
                    Response resp = ri.req.getErrorResponse();
                    resp.setResponseText("Request Timed Out");
                    processResponse(resp);
                }
            }
        }
    }

    /**
     * Constructs a new RequestTrackerPlugin.
     */
    public BasicRequestTrackerPlugin(){
        timer.schedule(new RequestTimeoutProcessor(), 1000, 1000);     
    }

    /**
     * This method allows you to bind a fall back request handler that will be called in
     * the case that no handler has been bound for a particular class of request.
     * @param requestHandler the handler to call when a Request doesn't have a bound handler
     */
    public void bindDefaultRequestHandler(DmpRequestProcessorIF requestHandler)
    {
        defaultRequestHandler = requestHandler;
    }

    /**
     * This method binds a request processor to a particular class that's derived from the
     * the DMP Request class. Generally speaking, the plugin that implements the CacheIF will
     * register handlers for the Get, Set, Create and Delete requests; these are the standard CRUD
     * operations. However, since the Dark Matter Protocol is extensible, you may have to register
     * other handlers for new types of requests.
     * @param clazz The class of Request handled by the processor.
     * @param requestProcessor The request processor.
     */
    public void bindRequestProcessor(Class<? extends Request> clazz, DmpRequestProcessorIF requestProcessor)
    {
        synchronized (requestProcessors)
        {
            LinkedList<DmpRequestProcessorIF> procList = requestProcessors.get(clazz);
            if (procList == null)
            {
                procList = new LinkedList<DmpRequestProcessorIF>();
                requestProcessors.put(clazz, procList);
            }
            procList.add(requestProcessor);
        }
    }

    /**
     * This method allows you to submit a request and receive asynchronous responses via the
     * response handler. In this case the default timeout of 2 seconds is applied to the request.
     * @param req The request.
     * @param responseHandler Where to send the responses.
     * @return the integer ID of this processing operation.
     */
    public int processRequest(Request req, DmpResponseHandlerIF responseHandler)
    {
        return processRequest(req, responseHandler, 2);
    }

    /**
     * This method allows for synchronous processing of a request; this is generally used when
     * service DMP requests that arrive via GWT RPC. Most operations will have a single response,
     * but, in cases where asynchronous responses can occur e.g. when a GetRequest has a blockingFactor
     * specified or for ActionRequests, the subsequent responses will be relayed via the event
     * channel for the client.
     * @param req The request.
     * @param asyncResponseHandler The handler that will handle any responses beyond the first one.
     * @param timeoutSeconds The timeout. This should be less than the standard HTTP session timeout.
     * @return The initial response. If there are any others, they will be sent to the response handler.
     */
    public Response processSynchronousRequest(Request req, DmpResponseHandlerIF asyncResponseHandler, int timeoutSeconds)
    {
        synchronized (req)
        {
            processRequest(req, synchResponseHandler, asyncResponseHandler, timeoutSeconds);
            try
            {
                req.wait();
            }
            catch (Exception e)
            {
                logger.error("Caught exception", e);
                return null;
            }
            return pendingResponses.remove(req.getLastRequestID());
        }
    }

    /**
     * This method allows you to submit a request with a specified timeout value.
     * @param req The request.
     * @param responseHandler Where to send the response.
     * @param timeoutSeconds The time out delay.
     */
    public int processRequest(Request req, DmpResponseHandlerIF responseHandler, int timeoutSeconds)
    {
        return processRequest(req, responseHandler, responseHandler, timeoutSeconds);
    }

    /**
     * When a request handler has a response available, it sends it back to the RequestTracker via this method.
     * @param resp A response for an outstanding request.
     */
    public void processResponse(Response resp)
    {
        if (resp.getRequestIDSize() == 0)
        {
            logger.debug("Got Response with no id: " + resp.toOIF());
            return;
        }
       
        int requestId = resp.getRequestIDSize() == 1 ? resp.getLastRequestID() : resp.removeLastRequestID();
       
        logger.debug("Handling response for tracking ID: " + requestId);
       
        DmpResponseHandlerIF responseHandler = null;
        RequestInfo ri = null;
        synchronized (outstandingRequests)
        {
            ri = resp.isLastResponse() ? outstandingRequests.remove(requestId) : outstandingRequests.get(requestId);
            if (ri == null)
            {
                logger.error("No record of request for " + resp.toOIF());
                return;
            }
            responseHandler = ri.responseHandler;
            if (ri.responseHandler == ri.firstResponseHandler) ri.responseHandler = ri.asyncResponseHandler;
        }
        responseHandler.handleResponse(ri.req, resp);
    }


   
   
    private int processRequest(Request req, DmpResponseHandlerIF firstResponseHandler, DmpResponseHandlerIF asyncResponseHandler, int timeoutSeconds)
    {
      DmpRequestProcessorIF requestProcessor = defaultRequestHandler;
        synchronized (requestProcessors)
        {
            LinkedList<DmpRequestProcessorIF> procList = requestProcessors.get(req.getClass());
            if (procList != null)
            {
                for (DmpRequestProcessorIF processor : procList)
                    if (processor.acceptRequest(req)) requestProcessor = processor;
               
                if (requestProcessor == defaultRequestHandler)
                  logger.debug("None of the request processors bound for " + req.getClass().getSimpleName() + " accepted the request");
            }
        }
       
        if (requestProcessor == null)
        {
//            logger.error("No request processor found for " + req.toOIF());
            return -1;
        }
       
        return trackRequest(req, firstResponseHandler, asyncResponseHandler, timeoutSeconds, requestProcessor);
    }

//    private int trackRequest(Request req, DmpResponseHandlerIF responseHandler, DmpRequestProcessorIF requestProcessor)
//    {
//        return trackRequest(req, responseHandler, 2, requestProcessor);
//    }

//    private int trackRequest(Request req, DmpResponseHandlerIF responseHandler, int timeoutSeconds, DmpRequestProcessorIF requestProcessor)
//    {
//        return trackRequest(req, responseHandler, responseHandler, timeoutSeconds, requestProcessor);
//    }

    private int trackRequest(Request req, DmpResponseHandlerIF firstResponseHandler, DmpResponseHandlerIF asyncResponseHandler, int timeoutSeconds, DmpRequestProcessorIF requestProcessor)
    {
      logger.debug("Tracking " + req.getConstructionClassName());
     
        synchronized (outstandingRequests)
        {
            nextRequestId++;
           
          logger.debug("Tracking " + req.getConstructionClassName() + " request with tracking ID " + nextRequestId);
         
            req.addRequestID(nextRequestId);
            req.setTimeMS(System.currentTimeMillis());
            outstandingRequests.put(nextRequestId, new RequestInfo(req, firstResponseHandler, asyncResponseHandler, System.currentTimeMillis() + (timeoutSeconds * 1000)));
        }
        requestProcessor.processRequest(req);
        return req.getLastRequestID();
    }

    private class SynchResponseHandler implements DmpResponseHandlerIF
    {
        public void handleResponse(Request req, Response resp)
        {
            synchronized (req)
            {
                pendingResponses.put(req.getLastRequestID(), resp);
                req.notify();
            }
        }
    }

}
TOP

Related Classes of org.dmd.dmp.server.servlet.base.plugins.BasicRequestTrackerPlugin$RequestInfo

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.