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

org.jgroups.stack.Protocol Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

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


package org.jgroups.stack;


import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.jmx.ResourceDMBean;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.TP;
import org.jgroups.util.MessageBatch;
import org.jgroups.util.SocketFactory;
import org.jgroups.util.ThreadFactory;
import org.jgroups.util.Util;
import org.w3c.dom.Node;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


/**
 * The Protocol class provides a set of common services for protocol layers. Each layer has to
 * be a subclass of Protocol and override a number of methods (typically just up(),
 * down() and getName(). Layers are stacked in a certain order to form
 * a protocol stack. Events are passed from lower
 * layers to upper ones and vice versa. E.g. a Message received by the UDP layer at the bottom
 * will be passed to its higher layer as an Event. That layer will in turn pass the Event to
 * its layer and so on, until a layer handles the Message and sends a response or discards it,
 * the former resulting in another Event being passed down the stack.
 * 

* The important thing to bear in mind is that Events have to passed on between layers in FIFO * order which is guaranteed by the Protocol implementation and must be guranteed by subclasses * implementing their on Event queuing.

* Note that each class implementing interface Protocol MUST provide an empty, public * constructor ! * * @author Bela Ban */ public abstract class Protocol { protected Protocol up_prot, down_prot; protected ProtocolStack stack; @Property(description="Determines whether to collect statistics (and expose them via JMX). Default is true",writable=true) protected boolean stats=true; @Property(description="Enables ergonomics: dynamically find the best values for properties at runtime") protected boolean ergonomics=true; /** The name of the protocol. Is by default set to the protocol's classname. This property should rarely need to * be set, e.g. only in cases where we want to create more than 1 protocol of the same class in the same stack */ @Property(name="name",description="Give the protocol a different name if needed so we can have multiple " + "instances of it in the same stack (also change ID)",writable=false) protected String name=getClass().getSimpleName(); @Property(description="Give the protocol a different ID if needed so we can have multiple " + "instances of it in the same stack",writable=false) protected short id=ClassConfigurator.getProtocolId(getClass()); protected final Log log=LogFactory.getLog(this.getClass()); /** * Sets the level of a logger. This method is used to dynamically change the logging level of a * running system, e.g. via JMX. The appender of a level needs to exist. * @param level The new level. Valid values are "fatal", "error", "warn", "info", "debug", "trace" * (capitalization not relevant) */ public Protocol setLevel(String level) {log.setLevel(level); return this;} @Property(name="level", description="logger level (see javadocs)") public String getLevel() {return log.getLevel();} public Protocol level(String level) {this.log.setLevel(level); return this;} public boolean isErgonomics() {return ergonomics;} public Protocol setErgonomics(boolean ergonomics) {this.ergonomics=ergonomics; return this;} public ProtocolStack getProtocolStack() {return stack;} public boolean statsEnabled() {return stats;} public void enableStats(boolean flag) {stats=flag;} public String getName() {return name;} public short getId() {return id;} public Protocol setId(short id) {this.id=id; return this;} public Protocol getUpProtocol() {return up_prot;} public Protocol getDownProtocol() {return down_prot;} public Protocol setUpProtocol(Protocol prot) {this.up_prot=prot; return this;} public Protocol setDownProtocol(Protocol prot) {this.down_prot=prot; return this;} public Protocol setProtocolStack(ProtocolStack s) {this.stack=s; return this;} public Object getValue(String name) { if(name == null) return null; Field field=Util.getField(getClass(), name); if(field == null) throw new IllegalArgumentException("field \"" + name + "\n not found"); return Util.getField(field, this); } public Protocol setValues(Map values) { if(values == null) return this; for(Map.Entry entry: values.entrySet()) { String attrname=entry.getKey(); Object value=entry.getValue(); Field field=Util.getField(getClass(), attrname); if(field != null) { Util.setField(field, this, value); } } return this; } public Protocol setValue(String name, Object value) { if(name == null || value == null) return this; Field field=Util.getField(getClass(), name); if(field == null) throw new IllegalArgumentException("field \"" + name + "\n not found"); Property prop=field.getAnnotation(Property.class); if(prop != null) { String deprecated_msg=prop.deprecatedMessage(); if(deprecated_msg != null && !deprecated_msg.isEmpty()) log.warn("Field " + getName() + "." + name + " is deprecated: " + deprecated_msg); } Util.setField(field, this, value); return this; } /** * After configuring the protocol itself from the properties defined in the XML config, a protocol might have * additional objects which need to be configured. This callback allows a protocol developer to configure those * other objects. This call is guaranteed to be invoked after the protocol itself has * been configured. See AUTH for an example. * @return */ protected List getConfigurableObjects() {return null;} /** Called by the XML parser when subelements are found in the configuration of a protocol. This allows * a protocol to define protocol-specific information and to parse it */ public void parse(Node node) throws Exception {;} /** Returns the protocol IDs of all protocols above this one (excluding the current protocol) */ public short[] getIdsAbove() { short[] retval; List ids=new ArrayList<>(); Protocol current=up_prot; while(current != null) { ids.add(current.getId()); current=current.up_prot; } retval=new short[ids.size()]; for(int i=0; i < ids.size(); i++) retval[i]=ids.get(i); return retval; } protected TP getTransport() { Protocol retval=this; while(retval != null && retval.down_prot != null) { retval=retval.down_prot; } return (TP)retval; } /** Supposed to be overwritten by subclasses. Usually the transport returns a valid non-null thread factory, but * thread factories can also be created by individual protocols * @return */ public ThreadFactory getThreadFactory() { return down_prot != null? down_prot.getThreadFactory(): null; } /** * Returns the SocketFactory associated with this protocol, if overridden in a subclass, or passes the call down * @return SocketFactory */ public SocketFactory getSocketFactory() { return down_prot != null? down_prot.getSocketFactory() : null; } /** * Sets a SocketFactory. Socket factories are typically provided by the transport ({@link org.jgroups.protocols.TP}) * or {@link org.jgroups.protocols.TP.ProtocolAdapter} * @param factory */ public void setSocketFactory(SocketFactory factory) { if(down_prot != null) down_prot.setSocketFactory(factory); } @ManagedOperation(description="Resets all stats") public void resetStatistics() {resetStats();} public void resetStats() { ; } public String printStats() { return null; } public Map dumpStats() { HashMap map=new HashMap<>(); for(Class clazz=this.getClass();clazz != null;clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(Field field: fields) { if(field.isAnnotationPresent(ManagedAttribute.class) || (field.isAnnotationPresent(Property.class) && field.getAnnotation(Property.class).exposeAsManagedAttribute())) { ManagedAttribute attr_annotation=field.getAnnotation(ManagedAttribute.class); Property prop=field.getAnnotation(Property.class); String attr_name=attr_annotation != null? attr_annotation.name() : prop != null? prop.name() : null; if(attr_name != null && !attr_name.trim().isEmpty()) attr_name=attr_name.trim(); else attr_name=field.getName(); try { field.setAccessible(true); Object value=field.get(this); map.put(attr_name, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (field) " + attr_name, e); } } } Method[] methods=this.getClass().getMethods(); for(Method method: methods) { if(method.isAnnotationPresent(ManagedAttribute.class) || (method.isAnnotationPresent(Property.class) && method.getAnnotation(Property.class).exposeAsManagedAttribute())) { ManagedAttribute attr_annotation=method.getAnnotation(ManagedAttribute.class); Property prop=method.getAnnotation(Property.class); String method_name=attr_annotation != null? attr_annotation.name() : prop != null? prop.name() : null; if(method_name != null && !method_name.trim().isEmpty()) method_name=method_name.trim(); else { String field_name=Util.methodNameToAttributeName(method.getName()); method_name=Util.attributeNameToMethodName(field_name); } if(ResourceDMBean.isGetMethod(method)) { try { Object value=method.invoke(this); String attributeName=Util.methodNameToAttributeName(method_name); map.put(attributeName, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (method) " + method_name,e); } } } } } return map; } /** * Called after instance has been created (null constructor) and before protocol is started. * Properties are already set. Other protocols are not yet connected and events cannot yet be sent. * @exception Exception Thrown if protocol cannot be initialized successfully. This will cause the * ProtocolStack to fail, so the channel constructor will throw an exception */ public void init() throws Exception { short real_id=ClassConfigurator.getProtocolId(getClass()); if(real_id > 0 && id != real_id) name=name+"-"+id; } /** * This method is called on a {@link org.jgroups.Channel#connect(String)}. Starts work. * Protocols are connected and queues are ready to receive events. * Will be called from bottom to top. This call will replace * the START and START_OK events. * @exception Exception Thrown if protocol cannot be started successfully. This will cause the ProtocolStack * to fail, so {@link org.jgroups.Channel#connect(String)} will throw an exception */ public void start() throws Exception { } /** * This method is called on a {@link org.jgroups.Channel#disconnect()}. Stops work (e.g. by closing multicast socket). * Will be called from top to bottom. This means that at the time of the method invocation the * neighbor protocol below is still working. This method will replace the * STOP, STOP_OK, CLEANUP and CLEANUP_OK events. The ProtocolStack guarantees that * when this method is called all messages in the down queue will have been flushed */ public void stop() { } /** * This method is called on a {@link org.jgroups.Channel#close()}. * Does some cleanup; after the call the VM will terminate */ public void destroy() { } /** List of events that are required to be answered by some layer above */ public List requiredUpServices() { return null; } /** List of events that are required to be answered by some layer below */ public List requiredDownServices() { return null; } /** List of events that are provided to layers above (they will be handled when sent down from above) */ public List providedUpServices() { return null; } /** List of events that are provided to layers below (they will be handled when sent down below) */ public List providedDownServices() { return null; } /** Returns all services provided by protocols below the current protocol */ public final List getDownServices() { List retval=new ArrayList<>(); Protocol prot=down_prot; while(prot != null) { List tmp=prot.providedUpServices(); if(tmp != null && !tmp.isEmpty()) retval.addAll(tmp); prot=prot.down_prot; } return retval; } /** Returns all services provided by the protocols above the current protocol */ public final List getUpServices() { List retval=new ArrayList<>(); Protocol prot=up_prot; while(prot != null) { List tmp=prot.providedDownServices(); if(tmp != null && !tmp.isEmpty()) retval.addAll(tmp); prot=prot.up_prot; } return retval; } /** * An event was received from the layer below. Usually the current layer will want to examine * the event type and - depending on its type - perform some computation * (e.g. removing headers from a MSG event type, or updating the internal membership list * when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down * the stack using down_prot.down() or c) the event (or another event) is sent up * the stack using up_prot.up(). */ public Object up(Event evt) { return up_prot.up(evt); } /** * Called by the default implementation of {@link #up(org.jgroups.util.MessageBatch)} for each message to determine * if the message should be removed from the message batch (and handled by the current protocol) or not. * @param msg The message. Guaranteed to be non-null * @return True if the message should be handled by this protocol (will be removed from the batch), false if the * message should remain in the batch and be passed up.

* The default implementation tries to find a header matching the current protocol's ID and returns true if there * is a match, or false otherwise */ protected boolean accept(Message msg) { short tmp_id=getId(); return tmp_id > 0 && msg.getHeader(tmp_id) != null; } /** * Sends up a multiple messages in a {@link MessageBatch}. The sender of the batch is always the same, and so is the * destination (null == multicast messages). Messages in a batch can be OOB messages, regular messages, or mixed * messages, although the transport itself will create initial MessageBatches that contain only either OOB or * regular messages.

* The default processing below sends messages up the stack individually, based on a matching criteria * (calling {@link #accept(org.jgroups.Message)}), and - if true - calls {@link #up(org.jgroups.Event)} * for that message and removes the message. If the batch is not empty, it is passed up, or else it is dropped.

* Subclasses should check if there are any messages destined for them (e.g. using * {@link MessageBatch#getMatchingMessages(short,boolean)}), then possibly remove and process them and finally pass * the batch up to the next protocol. Protocols can also modify messages in place, e.g. ENCRYPT could decrypt all * encrypted messages in the batch, not remove them, and pass the batch up when done. * @param batch The message batch */ public void up(MessageBatch batch) { for(Iterator it=batch.iterator(); it.hasNext();) { Message msg=it.next(); if(msg != null && accept(msg)) { it.remove(); try { up(new Event(Event.MSG, msg)); } catch(Throwable t) { log.error(Util.getMessage("PassUpFailure"), t); } } } if(!batch.isEmpty()) up_prot.up(batch); } /** * An event is to be sent down the stack. The layer may want to examine its type and perform * some action on it, depending on the event's type. If the event is a message MSG, then * the layer may need to add a header to it (or do nothing at all) before sending it down * the stack using down_prot.down(). In case of a GET_ADDRESS event (which tries to * retrieve the stack's address from one of the bottom layers), the layer may need to send * a new response event back up the stack using up_prot.up(). */ public Object down(Event evt) { return down_prot.down(evt); } }