package org.jgroups.stack; import org.jgroups.Event; 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.SocketFactory; import org.jgroups.util.ThreadFactory; import org.jgroups.util.Util; 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 <code>up()</code>, * <code>down()</code> and <code>getName()</code>. Layers are stacked in a certain order to form * a protocol stack. <a href=org.jgroups.Event.html>Events</a> 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. * <p/> * 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.<p> * <b>Note that each class implementing interface Protocol MUST provide an empty, public * constructor !</b> * * @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) */ @Property(name="level", description="Sets the logger level (see javadocs)") public void setLevel(String level) { log.setLevel(level); } public String getLevel() { return log.getLevel(); } public boolean isErgonomics() { return ergonomics; } public void setErgonomics(boolean ergonomics) { this.ergonomics=ergonomics; } 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<String,Object> values) { if(values == null) return this; for(Map.Entry<String,Object> 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"); Util.setField(field, this, value); return this; } public ProtocolStack getProtocolStack() { return stack; } /** * 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 <em>after</em> the protocol itself has * been configured. See AUTH for an example. * @return */ protected List<Object> getConfigurableObjects() { return null; } 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); } public boolean statsEnabled() { return stats; } public void enableStats(boolean flag) { stats=flag; } @ManagedOperation(description="Resets all stats") public void resetStatistics() {resetStats();} public void resetStats() { ; } public String printStats() { return null; } public Map<String,Object> dumpStats() { HashMap<String,Object> map=new HashMap<String,Object>(); 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())) { String attributeName=field.getName(); try { field.setAccessible(true); Object value=field.get(this); map.put(attributeName, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (field) " + attributeName,e); } } } Method[] methods=this.getClass().getMethods(); for(Method method: methods) { if(method.isAnnotationPresent(ManagedAttribute.class) || (method.isAnnotationPresent(Property.class) && method.getAnnotation(Property.class).exposeAsManagedAttribute())) { String method_name=method.getName(); if(method_name.startsWith("is") || method_name.startsWith("get")) { 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); } } else if(method_name.startsWith("set")) { String stem=method_name.substring(3); Method getter=ResourceDMBean.findGetter(getClass(), stem); if(getter != null) { try { Object value=getter.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 { } /** * 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 <em>from bottom to top</em>. This call will replace * the <b>START</b> and <b>START_OK</b> 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 <em>from top to bottom</em>. This means that at the time of the method invocation the * neighbor protocol below is still working. This method will replace the * <b>STOP</b>, <b>STOP_OK</b>, <b>CLEANUP</b> and <b>CLEANUP_OK</b> 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<Integer> requiredUpServices() { return null; } /** List of events that are required to be answered by some layer below */ public List<Integer> requiredDownServices() { return null; } /** List of events that are provided to layers above (they will be handled when sent down from above) */ public List<Integer> providedUpServices() { return null; } /** List of events that are provided to layers below (they will be handled when sent down below) */ public List<Integer> providedDownServices() { return null; } /** Returns all services provided by protocols below the current protocol */ public final List<Integer> getDownServices() { List<Integer> retval=new ArrayList<Integer>(); Protocol prot=down_prot; while(prot != null) { List<Integer> 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<Integer> getUpServices() { List<Integer> retval=new ArrayList<Integer>(); Protocol prot=up_prot; while(prot != null) { List<Integer> tmp=prot.providedDownServices(); if(tmp != null && !tmp.isEmpty()) retval.addAll(tmp); prot=prot.up_prot; } return retval; } /** All protocol names have to be unique ! */ public String getName() { return name; } public short getId() { return id; } public void setId(short id) { this.id=id; } public Protocol getUpProtocol() { return up_prot; } public Protocol getDownProtocol() { return down_prot; } public void setUpProtocol(Protocol up_prot) { this.up_prot=up_prot; } public void setDownProtocol(Protocol down_prot) { this.down_prot=down_prot; } public void setProtocolStack(ProtocolStack stack) { this.stack=stack; } /** * 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 <code>down_prot.down()</code> or c) the event (or another event) is sent up * the stack using <code>up_prot.up()</code>. */ public Object up(Event evt) { return up_prot.up(evt); } /** * 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 <code>down_prot.down()</code>. 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 <code>up_prot.up()</code>. */ public Object down(Event evt) { return down_prot.down(evt); } }