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