/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.clients.fcp; import freenet.node.Node; import freenet.pluginmanager.PluginManager; import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; /** * This class produces the network format for a FCP message which is send from a FCP server * plugin to a FCP client.<br> * It is the inverse of {@link FCPPluginClientMessage} which parses the on-network format of client * to server messages.<br><br> * * There is a similar class {@link FCPPluginMessage} which serves as a container of FCP plugin * messages which are produced and consumed by the actual server and client plugin implementations. * Consider this class here as an internal representation of FCP plugin messages used solely * for encoding server-to-client messages, while the other one is the external representation used * for both server and client messages. * * ATTENTION: The on-network name of this message is different: It is {@value #NAME}. The class * previously had the same name but it was decided to rename it: Previously, it was only allowed * for the server to send messages to the client as a direct <i>reply</i> to a message. Nowadays, * the server can send messages to the client any time he wants, even if there hasn't been a client * message for a long time. So the class was renamed to FCPPluginServerMessage to prevent the * misconception that it would be reply only, and also to make the name symmetrical to class * {@link FCPPluginClientMessage}. To stay backward compatible, it was decided to keep the raw * network message name as is. <br> * TODO: Would it technically be possible to add a second name to the on-network data so we * can get rid of the old name after a transition period?<br><br> * * @link FCPPluginConnection * FCPPluginConnection gives an overview of how plugin messaging works in general. * @link FCPPluginConnectionImpl * FCPPluginConnectionImpl gives an overview of the internal code paths which messages take. * @author * saces * @author * xor (xor@freenetproject.org) */ public class FCPPluginServerMessage extends DataCarryingMessage { /** * On-network format name of the message. * * ATTENTION: This one is different to the class name. For an explanation, see the class-level * JavaDoc {@link FCPPluginServerMessage}. */ private static final String NAME = "FCPPluginReply"; public static final String PARAM_PREFIX = "Replies"; /** @see FCPPluginMessage#data */ private final long dataLength; /** @see PluginManager#getPluginFCPServer(String) */ private final String plugname; /** @see FCPPluginMessage#identifier */ private final String identifier; /** @see FCPPluginMessage#params */ private final SimpleFieldSet plugparams; /** @see FCPPluginMessage#success */ private final Boolean success; /** @see FCPPluginMessage#errorCode */ private final String errorCode; /** @see FCPPluginMessage#errorMessage */ private final String errorMessage; /** * @deprecated * Use {@link #FCPPluginServerMessage(String, String, SimpleFieldSet, Bucket, Boolean, * String, String)}.<br><br> * * <b>ATTENTION:</b> Upon removal of this constructor, you should remove the backend * constructor so the only remaining constructor is the one which consumes a * {@link FCPPluginMessage}. Then you should remove all the member variables from this class * which duplicate the members of that class, and instead store a reference to an object of * the other class. */ @Deprecated public FCPPluginServerMessage(String pluginname, String identifier2, SimpleFieldSet fs, Bucket bucket2) { this(pluginname, identifier2, fs, bucket2, null, null, null); } /** * @param pluginname * The class name of the plugin which is sending the message.<br> * Must not be null.<br> * See {@link PluginManager#getPluginInfoByClassName(String)}. */ public FCPPluginServerMessage(String pluginname, FCPPluginMessage message) { this(pluginname, message.identifier, message.params, message.data, message.success, message.errorCode, message.errorMessage); assert(pluginname != null); } /** * The parameters match the member variables of {@link FCPPluginMessage}, and thus their JavaDoc * applies. */ public FCPPluginServerMessage(String pluginname, String identifier2, SimpleFieldSet fs, Bucket bucket2, Boolean success, String errorCode, String errorMessage) { bucket = bucket2; if (bucket == null) dataLength = -1; else { bucket.setReadOnly(); dataLength = bucket.size(); } plugname = pluginname; identifier = identifier2; plugparams = fs; this.success = success; this.errorCode = errorCode; this.errorMessage = errorMessage; } @Override String getIdentifier() { return identifier; } @Override boolean isGlobal() { return false; } @Override long dataLength() { return dataLength; } @Override String getEndString() { if (dataLength() > 0) return "Data"; else return "EndMessage"; } @Override public SimpleFieldSet getFieldSet() { SimpleFieldSet sfs = new SimpleFieldSet(true); sfs.putSingle("PluginName", plugname); sfs.putSingle("Identifier", identifier); if (dataLength() > 0) sfs.put("DataLength", dataLength()); // The sfs.put() would throw IllegalArgumentException if plugparams.isEmpty() == true. if(plugparams != null && !plugparams.isEmpty()) { sfs.put(PARAM_PREFIX, plugparams); } if(success != null) { sfs.put("Success", success); if(!success && errorCode != null) { sfs.putSingle("ErrorCode", errorCode); if(errorMessage != null) { sfs.putSingle("ErrorMessage", errorMessage); } } } return sfs; } @Override public String getName() { return NAME; } @Override public void run(FCPConnectionHandler handler, Node node) throws MessageInvalidException { throw new MessageInvalidException(ProtocolErrorMessage.INVALID_MESSAGE, NAME + " goes from server to client not the other way around", null, false); } }