AbstractStubDataCommandHandler.java revision 848932d9e7c6953b3c345c9aa6b0b6c3cfe20d79
1/* 2 * Copyright 2007 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockftpserver.stub.command; 17 18import org.mockftpserver.core.command.*; 19import org.mockftpserver.core.session.Session; 20import org.mockftpserver.core.util.AssertFailedException; 21 22/** 23 * Abstract superclass for CommandHandlers that read from or write to the data connection. 24 * <p/> 25 * Return two replies on the control connection: by default a reply code of 150 before the 26 * data transfer across the data connection and another reply of 226 after the data transfer 27 * is complete. 28 * <p/> 29 * This class implements the <i>Template Method</i> pattern. Subclasses must implement the abstract 30 * <code>processData</code> method to perform read or writes across the data connection. 31 * <p/> 32 * Subclasses can optionally override the {@link #beforeProcessData(Command, Session, InvocationRecord)} 33 * method for logic before the data transfer or the {@link #afterProcessData(Command, Session, InvocationRecord)} 34 * method for logic after the data transfer. 35 * <p/> 36 * Subclasses can optionally override the reply code and/or text for the initial reply (before 37 * the data transfer across the data connection) by calling {@link #setPreliminaryReplyCode(int)}, 38 * {@link #setPreliminaryReplyMessageKey(String)} and/or {@link #setPreliminaryReplyText(String)} 39 * methods. 40 * <p/> 41 * Subclasses can optionally override the reply code and/or text for the final reply (after the 42 * the data transfer is complete) by calling {@link #setFinalReplyCode(int)}, 43 * {@link #setFinalReplyMessageKey(String)} and/or {@link #setFinalReplyText(String)} methods. 44 * 45 * @author Chris Mair 46 * @version $Revision$ - $Date$ 47 */ 48public abstract class AbstractStubDataCommandHandler extends AbstractTrackingCommandHandler implements CommandHandler { 49 50 // The completion reply code sent before the data transfer 51 protected int preliminaryReplyCode = 0; 52 53 // The text for the preliminary reply. If null, use the default message associated with the reply code. 54 // If not null, this value overrides the preliminaryReplyMessageKey - i.e., this text is used instead of 55 // a localized message. 56 protected String preliminaryReplyText = null; 57 58 // The message key for the preliminary reply text. If null, use the default message associated with 59 // the reply code. 60 protected String preliminaryReplyMessageKey = null; 61 62 // The completion reply code sent after data transfer 63 protected int finalReplyCode = 0; 64 65 // The text for the completion reply. If null, use the default message associated with the reply code. 66 // If not null, this value overrides the finalReplyMessageKey - i.e., this text is used instead of 67 // a localized message. 68 protected String finalReplyText = null; 69 70 // The message key for the completion reply text. If null, use the default message associated with the reply code 71 protected String finalReplyMessageKey = null; 72 73 /** 74 * Constructor. Initialize the preliminary and final reply code. 75 */ 76 protected AbstractStubDataCommandHandler() { 77 setPreliminaryReplyCode(ReplyCodes.TRANSFER_DATA_INITIAL_OK); 78 setFinalReplyCode(ReplyCodes.TRANSFER_DATA_FINAL_OK); 79 } 80 81 /** 82 * Handle the command. Perform the following steps: 83 * <ol> 84 * <li>Invoke the <code>beforeProcessData()</code> method</li> 85 * <li>Open the data connection</li> 86 * <li>Send an preliminary reply, default reply code 150</li> 87 * <li>Invoke the <code>processData()</code> method</li> 88 * <li>Close the data connection</li> 89 * <li>Send the final reply, default reply code 226</li> 90 * <li>Invoke the <code>afterProcessData()</code> method</li> 91 * </ol> 92 * 93 * @see org.mockftpserver.core.command.AbstractTrackingCommandHandler#handleCommand(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord) 94 */ 95 public final void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 96 97 beforeProcessData(command, session, invocationRecord); 98 99 sendPreliminaryReply(session); 100 session.openDataConnection(); 101 processData(command, session, invocationRecord); 102 session.closeDataConnection(); 103 sendFinalReply(session); 104 105 afterProcessData(command, session, invocationRecord); 106 } 107 108 /** 109 * Send the final reply. The default implementation sends a reply code of 226 with the 110 * corresponding associated reply text. 111 * 112 * @param session - the Session 113 */ 114 protected void sendFinalReply(Session session) { 115 sendReply(session, finalReplyCode, finalReplyMessageKey, finalReplyText, null); 116 } 117 118 /** 119 * Perform any necessary logic before transferring data across the data connection. 120 * Do nothing by default. Subclasses should override to validate command parameters and 121 * store information in the InvocationRecord. 122 * 123 * @param command - the Command to be handled 124 * @param session - the session on which the Command was submitted 125 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 126 * handler-specific data to the InvocationRecord, as appropriate 127 * @throws Exception 128 */ 129 protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 130 // Do nothing by default 131 } 132 133 /** 134 * Abstract method placeholder for subclass transfer of data across the data connection. 135 * Subclasses must override. The data connection is opened before this method and is 136 * closed after this method completes. 137 * 138 * @param command - the Command to be handled 139 * @param session - the session on which the Command was submitted 140 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 141 * handler-specific data to the InvocationRecord, as appropriate 142 * @throws Exception 143 */ 144 protected abstract void processData(Command command, Session session, InvocationRecord invocationRecord) throws Exception; 145 146 /** 147 * Perform any necessary logic after transferring data across the data connection. 148 * Do nothing by default. 149 * 150 * @param command - the Command to be handled 151 * @param session - the session on which the Command was submitted 152 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 153 * handler-specific data to the InvocationRecord, as appropriate 154 * @throws Exception 155 */ 156 protected void afterProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 157 // Do nothing by default 158 } 159 160 /** 161 * Send the preliminary reply for this command on the control connection. 162 * 163 * @param session - the Session 164 */ 165 private void sendPreliminaryReply(Session session) { 166 sendReply(session, preliminaryReplyCode, preliminaryReplyMessageKey, preliminaryReplyText, null); 167 } 168 169 /** 170 * Set the completion reply code sent after data transfer 171 * 172 * @param finalReplyCode - the final reply code 173 * @throws AssertFailedException - if the finalReplyCode is invalid 174 */ 175 public void setFinalReplyCode(int finalReplyCode) { 176 assertValidReplyCode(finalReplyCode); 177 this.finalReplyCode = finalReplyCode; 178 } 179 180 /** 181 * Set the message key for the completion reply text sent after data transfer 182 * 183 * @param finalReplyMessageKey - the final reply message key 184 */ 185 public void setFinalReplyMessageKey(String finalReplyMessageKey) { 186 this.finalReplyMessageKey = finalReplyMessageKey; 187 } 188 189 /** 190 * Set the text of the completion reply sent after data transfer 191 * 192 * @param finalReplyText - the final reply text 193 */ 194 public void setFinalReplyText(String finalReplyText) { 195 this.finalReplyText = finalReplyText; 196 } 197 198 /** 199 * Set the completion reply code sent before data transfer 200 * 201 * @param preliminaryReplyCode - the preliminary reply code to set 202 * @throws AssertFailedException - if the preliminaryReplyCode is invalid 203 */ 204 public void setPreliminaryReplyCode(int preliminaryReplyCode) { 205 assertValidReplyCode(preliminaryReplyCode); 206 this.preliminaryReplyCode = preliminaryReplyCode; 207 } 208 209 /** 210 * Set the message key for the completion reply text sent before data transfer 211 * 212 * @param preliminaryReplyMessageKey - the preliminary reply message key 213 */ 214 public void setPreliminaryReplyMessageKey(String preliminaryReplyMessageKey) { 215 this.preliminaryReplyMessageKey = preliminaryReplyMessageKey; 216 } 217 218 /** 219 * Set the text of the completion reply sent before data transfer 220 * 221 * @param preliminaryReplyText - the preliminary reply text 222 */ 223 public void setPreliminaryReplyText(String preliminaryReplyText) { 224 this.preliminaryReplyText = preliminaryReplyText; 225 } 226 227} 228