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

org.jgroups.blocks.RequestCorrelator Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version

package org.jgroups.blocks;

import org.jgroups.*;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.TP;
import org.jgroups.protocols.relay.SiteMaster;
import org.jgroups.stack.DiagnosticsHandler;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Bits;
import org.jgroups.util.Buffer;
import org.jgroups.util.Util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.NotSerializableException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;


/**
 * Framework to send requests and receive matching responses (on request ID).
 * Multiple requests can be sent at a time. Whenever a response is received, the correct {@code Request} is looked up
 * (key = id) and its method {@code receiveResponse()} invoked.

* @author Bela Ban */ public class RequestCorrelator { /** The protocol layer to use to pass up/down messages. Can be either a Protocol or a Transport */ protected Protocol transport; /** The table of pending requests (keys=Long (request IDs), values=RequestEntry) */ protected final ConcurrentMap requests=Util.createConcurrentMap(); /** To generate unique request IDs */ protected static final AtomicLong REQUEST_ID=new AtomicLong(1); /** The handler for the incoming requests. It is called from inside the dispatcher thread */ protected RequestHandler request_handler; /** Possibility for an external marshaller to marshal/unmarshal responses */ protected RpcDispatcher.Marshaller marshaller; /** makes the instance unique (together with IDs) */ protected short corr_id=ClassConfigurator.getProtocolId(this.getClass()); /** The address of this group member */ protected Address local_addr; protected volatile View view; protected boolean started; /** Whether or not to use async dispatcher */ protected boolean async_dispatching; // send exceptions back wrapped in an {@link InvocationTargetException}, or not protected boolean wrap_exceptions=true; private final MyProbeHandler probe_handler=new MyProbeHandler(); protected static final Log log=LogFactory.getLog(RequestCorrelator.class); /** * Constructor. Uses transport to send messages. If {@code handler} is not null, all incoming requests will be * dispatched to it (via {@code handle(Message)}). * * @param corr_id Used to differentiate between different RequestCorrelators (e.g. in different protocol layers). * Has to be unique if multiple request correlators are used. * * @param transport Used to send/pass up requests. * * @param handler Request handler. Method {@code handle(Message)} will be called when a request is received. */ public RequestCorrelator(short corr_id, Protocol transport, RequestHandler handler, Address local_addr) { this.corr_id=corr_id; this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } public RequestCorrelator(Protocol transport, RequestHandler handler, Address local_addr) { this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } public void setRequestHandler(RequestHandler handler) { request_handler=handler; start(); } public Address getLocalAddress() {return local_addr;} public void setLocalAddress(Address a) {this.local_addr=a;} protected void removeEntry(long id) {requests.remove(id);} public RpcDispatcher.Marshaller getMarshaller() {return marshaller;} public void setMarshaller(RpcDispatcher.Marshaller marshaller) {this.marshaller=marshaller;} public boolean asyncDispatching() {return async_dispatching;} public RequestCorrelator asyncDispatching(boolean flag) {async_dispatching=flag; return this;} public boolean wrapExceptions() {return wrap_exceptions;} public RequestCorrelator wrapExceptions(boolean flag) {wrap_exceptions=flag; return this;} public void sendRequest(List

dest_mbrs, Message msg, Request req) throws Exception { sendRequest(dest_mbrs, msg, req, new RequestOptions().setAnycasting(false)); } /** * Sends a request to a group. If no response collector is given, no responses are expected (making the call asynchronous) * @param dest_mbrs The list of members who should receive the call. Usually a group RPC * is sent via multicast, but a receiver drops the request if its own address * is not in this list. Will not be used if it is null. * @param msg The request to be sent. The body of the message carries the request data * @param req A request (usually the object that invokes this method). Its methods {@code receiveResponse()} and * {@code suspect()} will be invoked when a message has been received or a member is suspected. */ public void sendRequest(Collection
dest_mbrs, Message msg, Request req, RequestOptions options) throws Exception { if(transport == null) { log.warn("transport is not available !"); return; } // i. Create the request correlator header and add it to the msg // ii. If a reply is expected (coll != null), add a coresponding entry in the pending requests table Header hdr=options.hasExclusionList()? new MultiDestinationHeader(Header.REQ, 0, this.corr_id, options.exclusionList()) : new Header(Header.REQ, 0, this.corr_id); msg.putHeader(this.corr_id, hdr); if(req != null) { long req_id=REQUEST_ID.getAndIncrement(); hdr.requestId(req_id); // set the request-id only for *synchronous RPCs* if(log.isTraceEnabled()) log.trace("%s: invoking multicast RPC [req-id=%d]", local_addr, req_id); requests.putIfAbsent(req_id, req); // make sure no view is received before we add ourself as a view handler (https://issues.jboss.org/browse/JGRP-1428) req.viewChange(view); } if(options.getAnycasting()) { if(options.useAnycastAddresses()) { Message copy=msg.copy(true); AnycastAddress dest=new AnycastAddress(dest_mbrs); copy.setDest(dest); transport.down(new Event(Event.MSG, copy)); } else { for(Address mbr: dest_mbrs) { Message copy=msg.copy(true); copy.setDest(mbr); if(!mbr.equals(local_addr) && copy.isTransientFlagSet(Message.TransientFlag.DONT_LOOPBACK)) copy.clearTransientFlag(Message.TransientFlag.DONT_LOOPBACK); transport.down(new Event(Event.MSG, copy)); } } } else transport.down(new Event(Event.MSG, msg)); } /** Sends a request to a single destination */ public void sendUnicastRequest(Address target, Message msg, Request req) throws Exception { if(transport == null) { if(log.isWarnEnabled()) log.warn("transport is not available !"); return; } // i. Create the request correlator header and add it to the msg // ii. If a reply is expected (coll != null), add a coresponding entry in the pending requests table // iii. If deadlock detection is enabled, set/update the call stack // iv. Pass the msg down to the protocol layer below Header hdr=new Header(Header.REQ, 0, this.corr_id); msg.putHeader(this.corr_id, hdr); if(req != null) { long req_id=REQUEST_ID.getAndIncrement(); hdr.requestId(req_id); // set the request-id only for *synchronous RPCs* if(log.isTraceEnabled()) log.trace("%s: invoking unicast RPC [req-id=%d] on %s", local_addr, req_id, target); requests.putIfAbsent(req_id, req); // make sure no view is received before we add ourself as a view handler (https://issues.jboss.org/browse/JGRP-1428) req.viewChange(view); } transport.down(new Event(Event.MSG, msg)); } /** * Used to signal that a certain request may be garbage collected as all responses have been received. */ public void done(long id) { removeEntry(id); } public void done(Request req) { if(req != null) requests.values().remove(req); } /** * Callback. *

* Called by the protocol below when a message has been received. The algorithm should test whether the message * is destined for us and, if not, pass it up to the next layer. Otherwise, it should remove the header and check * whether the message is a request or response. * In the first case, the message will be delivered to the request handler registered * (calling its {@code handle()} method), in the second case, the corresponding response collector is looked up and * the message delivered. * @param evt The event to be received * @return Whether or not the event was consumed. If true, don't pass message up, else pass it up */ public boolean receive(Event evt) { switch(evt.getType()) { case Event.SUSPECT: // don't wait for responses from faulty members receiveSuspect((Address)evt.getArg()); break; case Event.VIEW_CHANGE: // adjust number of responses to wait for receiveView((View)evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: setLocalAddress((Address)evt.getArg()); break; case Event.MSG: if(receiveMessage((Message)evt.getArg())) return true; // message was consumed, don't pass it up break; case Event.SITE_UNREACHABLE: SiteMaster site_master=(SiteMaster)evt.getArg(); String site=site_master.getSite(); setSiteUnreachable(site); break; // let others have a stab at this event, too } return false; } public final void start() { started=true; } public void stop() { started=false; for(Request req: requests.values()) req.transportClosed(); requests.clear(); } public void registerProbeHandler(TP transport) { if(transport != null) transport.registerProbeHandler(probe_handler); } public void unregisterProbeHandler(TP transport) { if(transport != null) transport.unregisterProbeHandler(probe_handler); } // ....................................................................... /** * Event.SUSPECT event received from a layer below. *

* All response collectors currently registered will be notified that {@code mbr} may have crashed, so they won't * wait for its response. */ public void receiveSuspect(Address mbr) { if(mbr == null) return; log.debug("suspect=" + mbr); // copy so we don't run into bug #761804 - Bela June 27 2003 // copy=new ArrayList(requests.values()); // removed because ConcurrentReaderHashMap can tolerate concurrent mods (bela May 8 2006) for(Request req: requests.values()) { if(req != null) req.suspect(mbr); } } /** An entire site is down; mark all requests that point to that site as unreachable (used by RELAY2) */ public void setSiteUnreachable(String site) { for(Request req: requests.values()) { if(req != null) req.siteUnreachable(site); } } /** * Event.VIEW_CHANGE event received from a layer below. *

* Mark all responses from members that are not in new_view as * NOT_RECEIVED. * */ public void receiveView(View new_view) { // copy so we don't run into bug #761804 - Bela June 27 2003 // copy=new ArrayList(requests.values()); // removed because ConcurrentHashMap can tolerate concurrent mods (bela May 8 2006) view=new_view; // move this before the iteration (JGRP-1428) for(Request req: requests.values()) { if(req != null) req.viewChange(new_view); } } /** * Handles a message coming from a layer below * @return true if the message was consumed, don't pass it further up, else false */ public boolean receiveMessage(Message msg) { Header hdr=(Header)msg.getHeader(this.corr_id); if(hdr == null) return false; // Check if the message was sent by a request correlator with the same name; // there may be multiple request correlators in the same protocol stack if(hdr.corrId != this.corr_id) { if(log.isTraceEnabled()) log.trace(new StringBuilder("id of request correlator header (").append(hdr.corrId). append(") is different from ours (").append(this.corr_id).append("). Msg not accepted, passed up")); return false; } if(hdr instanceof MultiDestinationHeader) { // if we are part of the exclusion list, then we discard the request (addressed to different members) Address[] exclusion_list=((MultiDestinationHeader)hdr).exclusion_list; if(exclusion_list != null && local_addr != null && Util.contains(local_addr, exclusion_list)) { if(log.isTraceEnabled()) log.trace("%s: discarded request from %s as we are in the exclusion list, hdr=", local_addr, msg.getSrc(), hdr); return true; // don't pass this message further up } } switch(hdr.type) { case Header.REQ: handleRequest(msg, hdr); break; case Header.RSP: case Header.EXC_RSP: Request req=requests.get(hdr.req_id); if(req != null) { boolean is_exception=hdr.type == Header.EXC_RSP; Address sender=msg.getSrc(); Object retval; byte[] buf=msg.getRawBuffer(); int offset=msg.getOffset(), length=msg.getLength(); try { retval=marshaller != null? marshaller.objectFromBuffer(buf, offset, length) : Util.objectFromByteBuffer(buf, offset, length); } catch(Exception e) { log.error(Util.getMessage("FailedUnmarshallingBufferIntoReturnValue"), e); retval=e; is_exception=true; } req.receiveResponse(retval, sender, is_exception); } break; default: log.error(Util.getMessage("HeaderSTypeIsNeitherREQNorRSP")); break; } return true; // message was consumed } // ....................................................................... /** * Handle a request msg for this correlator * @param req the request msg */ protected void handleRequest(Message req, Header hdr) { Object retval; boolean threw_exception=false; if(log.isTraceEnabled()) log.trace("calling (%s) with request %d", request_handler != null? request_handler.getClass().getName() : "null", hdr.req_id); if(async_dispatching && request_handler instanceof AsyncRequestHandler) { Response rsp=hdr.rspExpected()? new ResponseImpl(req, hdr.req_id) : null; try { ((AsyncRequestHandler)request_handler).handle(req, rsp); } catch(Throwable t) { if(rsp != null) rsp.send(wrap_exceptions ? new InvocationTargetException(t) : t, true); else log.error("%s: failed dispatching request asynchronously: %s", local_addr, t); } return; } try { retval=request_handler.handle(req); } catch(Throwable t) { threw_exception=true; retval=wrap_exceptions ? new InvocationTargetException(t) : t; } if(hdr.rspExpected()) sendReply(req, hdr.req_id, retval, threw_exception); } protected void sendReply(final Message req, final long req_id, Object reply, boolean is_exception) { Buffer rsp_buf; try { // retval could be an exception, or a real value rsp_buf=marshaller != null? marshaller.objectToBuffer(reply) : Util.objectToBuffer(reply); } catch(Throwable t) { try { // this call should succeed (all exceptions are serializable) rsp_buf=marshaller != null? marshaller.objectToBuffer(t) : Util.objectToBuffer(t); is_exception=true; } catch(NotSerializableException not_serializable) { if(log.isErrorEnabled()) log.error(Util.getMessage("FailedMarshallingRsp") + reply + "): not serializable"); return; } catch(Throwable tt) { if(log.isErrorEnabled()) log.error(Util.getMessage("FailedMarshallingRsp") + reply + "): " + tt); return; } } Message rsp=req.makeReply().setFlag(req.getFlags()).setBuffer(rsp_buf) .clearFlag(Message.Flag.RSVP, Message.Flag.SCOPED, Message.Flag.INTERNAL); // JGRP-1940 sendResponse(rsp, req_id, is_exception); } protected void sendResponse(Message rsp, long req_id, boolean is_exception) { prepareResponse(rsp); Header rsp_hdr=new Header(is_exception? Header.EXC_RSP : Header.RSP, req_id, corr_id); rsp.putHeader(corr_id, rsp_hdr); if(log.isTraceEnabled()) log.trace("sending rsp for %d to %s", req_id, rsp.getDest()); transport.down(new Event(Event.MSG, rsp)); } protected void prepareResponse(Message rsp) { ; } // ....................................................................... protected class ResponseImpl implements Response { protected final Message req; protected final long req_id; public ResponseImpl(Message req, long req_id) { this.req=req; this.req_id=req_id; } public void send(Object reply, boolean is_exception) { sendReply(req, req_id, reply, is_exception); } public void send(Message reply, boolean is_exception) { sendResponse(reply, req_id, is_exception); } } /** * The header for RequestCorrelator messages */ public static class Header extends org.jgroups.Header { public static final byte REQ = 0; public static final byte RSP = 1; public static final byte EXC_RSP = 2; // exception /** Type of header: request or reply */ public byte type; /** The request id (unique for each blocking request), 0 means no response is expected */ public long req_id; /** The unique ID of the associated RequestCorrelator */ public short corrId; public Header() {} /** * @param type type of header (REQ/RSP) * @param req_id id of this header relative to ids of other requests originating from the same correlator * @param corr_id The ID of the RequestCorrelator from which */ public Header(byte type, long req_id, short corr_id) { this.type=type; this.req_id=req_id; this.corrId=corr_id; } public Header requestId(long req_id) { if(this.req_id > 0) throw new IllegalStateException(String.format("request-id (%d) is already set: trying to set it again (%d)", this.req_id, req_id)); this.req_id=req_id; return this; } public boolean rspExpected() {return req_id > 0;} public String toString() { StringBuilder ret=new StringBuilder(); ret.append("corr_id=" + corrId + ", type="); switch(type) { case REQ: ret.append("REQ"); break; case RSP: ret.append("RSP"); break; case EXC_RSP: ret.append("EXC_RSP"); break; default: ret.append(""); } ret.append(", req_id=" + req_id).append(", rsp_expected=" + rspExpected()); return ret.toString(); } public void writeTo(DataOutput out) throws Exception { out.writeByte(type); Bits.writeLong(req_id, out); out.writeShort(corrId); } public void readFrom(DataInput in) throws Exception { type=in.readByte(); req_id=Bits.readLong(in); corrId=in.readShort(); } public int size() { return Global.BYTE_SIZE // type + Bits.size(req_id) // req_id + Global.SHORT_SIZE; // corrId } } public static final class MultiDestinationHeader extends Header { /** Contains a list of members who should not receive the request (others will drop). Ignored if null */ public Address[] exclusion_list; public MultiDestinationHeader() { } public MultiDestinationHeader(byte type, long id, short corr_id, Address[] exclusion_list) { super(type, id, corr_id); this.exclusion_list=exclusion_list; } public void writeTo(DataOutput out) throws Exception { super.writeTo(out); Util.writeAddresses(exclusion_list, out); } public void readFrom(DataInput in) throws Exception { super.readFrom(in); exclusion_list=Util.readAddresses(in); } public int size() { return (int)(super.size() + Util.size(exclusion_list)); } public String toString() { String str=super.toString(); if(exclusion_list != null) str=str+ ", exclusion_list=" + Arrays.toString(exclusion_list); return str; } } protected class MyProbeHandler implements DiagnosticsHandler.ProbeHandler { public Map handleProbe(String... keys) { if(requests == null) return null; Map retval=new HashMap<>(); for(String key: keys) { switch(key) { case "requests": StringBuilder sb=new StringBuilder(); for(Map.Entry entry: requests.entrySet()) sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); retval.put(key, sb.toString()); break; case "reqtable-info": retval.put(key, String.format("size=%d, next-id=%d", requests.size(), REQUEST_ID.get())); break; } } return retval; } public String[] supportedKeys() { return new String[]{"requests", "reqtable-info"}; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy