/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.protocol; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.communication.common.IdentifierException; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.common.NodeIdentifierContextHolder; import de.rcenvironment.core.communication.common.NodeIdentifierUtils; import de.rcenvironment.core.communication.model.NetworkMessage; /** * Semantic wrapper around the key-value metadata map of a {@link NetworkMessage}. * * @author Phillip Kroll * @author Robert Mischke */ public class MessageMetaData { private static final String KEY_MESSAGE_TYPE = "type"; private static final String KEY_MESSAGEID = "id"; private static final String KEY_SENDER = "src"; private static final String KEY_FINAL_RECIPIENT = "dst"; private static final String KEY_HOPCOUNT = "hopcount"; private static final String KEY_TRACE = "trace"; private Map<String, String> properties; private final Log log = LogFactory.getLog(MessageMetaData.class); public MessageMetaData() { this(new HashMap<String, String>()); } public MessageMetaData(Map<String, String> metaData) { properties = metaData; } /** * @return An empty instance. */ public static MessageMetaData create() { return new MessageMetaData(); } /** * @param metaData The data structure. * @return The instance created from a given data structure. */ public static MessageMetaData wrap(Map<String, String> metaData) { return new MessageMetaData(metaData); } /** * Factory method that clones the given map and wraps it into a {@link MessageMetaData}. * * @param metaData the map to clone and wrap * @return the generated {@link MessageMetaData} */ public static MessageMetaData cloneAndWrap(Map<String, String> metaData) { return new MessageMetaData(new HashMap<String, String>(metaData)); } /** * Returns the top-level type of this message. Examples of such types are RCP, health check or LSA. * * @return the String id of the message type */ public String getMessageType() { return getValue(KEY_MESSAGE_TYPE); } /** * Sets the message type. * * @param type the message type; should be one of the values in {@link ProtocolConstants} * @return itself (for chaining) */ public MessageMetaData setMessageType(String type) { setValue(KEY_MESSAGE_TYPE, type); return this; } /** * Clones and returns the internal metadata map. * * @return an independent clone of the internal map */ public Map<String, String> cloneData() { Map<String, String> clone = new HashMap<String, String>(); clone.putAll(properties); return clone; } /** * @return The map. */ public Map<String, String> getInnerMap() { return properties; } /** * @param key The key. * @return The value assigned to the key. */ public String getValue(String key) { return properties.get(key); } /** * TODO krol_ph: Enter comment! * * @param key The map key. * @param value The map value. * @return Itself. */ public MessageMetaData setValue(String key, String value) { properties.put(key, value); return this; } /** * Sets the final recipient of a message. Can be omitted for non-routed messages. * * @param receiver The receiver {@link InstanceNodeSessionId} * @return Itself. */ public MessageMetaData setFinalRecipient(InstanceNodeSessionId receiver) { setValue(KEY_FINAL_RECIPIENT, receiver.getInstanceNodeSessionIdString()); return this; } /** * Sets the {@link #KEY_SENDER} field. * * @param sender the sender to set * @return self */ public MessageMetaData setSender(InstanceNodeSessionId sender) { setValue(KEY_SENDER, sender.getInstanceNodeSessionIdString()); return this; } /** * Sets the {@link #KEY_MESSAGEID} field. * * @param id the message id to set * @return self */ public MessageMetaData setMessageId(String id) { setValue(KEY_MESSAGEID, id); return this; } /** * Adds a route tracing step (the node ids of either the sender or a traversed node when forwarding). * * @param newStep the step to append * @return Itself. */ public MessageMetaData addTraceStep(String newStep) { String oldValue = getValue(KEY_TRACE); if (oldValue == null) { setValue(KEY_TRACE, newStep); } else { setValue(KEY_TRACE, oldValue + "," + newStep); } return this; } /** * Increment hop count to check for maximum time to live. * * @return Itself. */ public MessageMetaData incHopCount() { if (properties.containsKey(KEY_HOPCOUNT)) { setValue(KEY_HOPCOUNT, Integer.toString(getHopCount() + 1)); } else { setValue(KEY_HOPCOUNT, "1"); } return this; } /** * @return The trace. */ public String getTrace() { return getValue(KEY_TRACE); } public String getFinalRecipientIdString() { return getValue(KEY_FINAL_RECIPIENT); } /** * @return The receiver. */ public InstanceNodeSessionId getFinalRecipient() { String idString = getFinalRecipientIdString(); if (idString != null) { return NodeIdentifierUtils.parseInstanceNodeSessionIdStringWithExceptionWrapping(idString); } else { return null; } } public String getSenderIdString() { return getValue(KEY_SENDER); } /** * @return The sender. */ public InstanceNodeSessionId getSender() { String idString = getSenderIdString(); if (idString != null) { try { return NodeIdentifierContextHolder.getDeserializationServiceForCurrentThread().parseInstanceNodeSessionIdString(idString); } catch (IdentifierException e) { // TODO >=8.0 improve? throw NodeIdentifierUtils.wrapIdentifierException(e); } } else { // TODO review handling of sender field - misc_ro log.debug("Returning 'null' node id for empty 'sender' field; message type=" + getMessageType()); return null; } } /** * @return true if a sender is defined */ public boolean hasSender() { return getSenderIdString() != null; } /** * @return The hash. */ public String getMessageId() { return getValue(KEY_MESSAGEID); } /** * @return The hop count. */ public int getHopCount() { try { return Integer.parseInt(getValue(KEY_HOPCOUNT)); } catch (NumberFormatException e) { return 0; } } }