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.AbstractCommandHandler; 19import org.mockftpserver.core.command.Command; 20import org.mockftpserver.core.command.CommandHandler; 21import org.mockftpserver.core.command.InvocationRecord; 22import org.mockftpserver.core.command.ReplyCodes; 23import org.mockftpserver.core.session.Session; 24import 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 * {@link #processData()} 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 * @version $Revision$ - $Date$ 50 * 51 * @author Chris Mair 52 */ 53public abstract class AbstractStubDataCommandHandler extends AbstractCommandHandler implements CommandHandler { 54 55 // The completion reply code sent before the data transfer 56 protected int preliminaryReplyCode = 0; 57 58 // The text for the preliminary reply. If null, use the default message associated with the reply code. 59 // If not null, this value overrides the preliminaryReplyMessageKey - i.e., this text is used instead of 60 // a localized message. 61 protected String preliminaryReplyText = null; 62 63 // The message key for the preliminary reply text. If null, use the default message associated with 64 // the reply code. 65 protected String preliminaryReplyMessageKey = null; 66 67 // The completion reply code sent after data transfer 68 protected int finalReplyCode = 0; 69 70 // The text for the completion reply. If null, use the default message associated with the reply code. 71 // If not null, this value overrides the finalReplyMessageKey - i.e., this text is used instead of 72 // a localized message. 73 protected String finalReplyText = null; 74 75 // The message key for the completion reply text. If null, use the default message associated with the reply code 76 protected String finalReplyMessageKey = null; 77 78 /** 79 * Constructor. Initialize the preliminary and final reply code. 80 */ 81 protected AbstractStubDataCommandHandler() { 82 setPreliminaryReplyCode(ReplyCodes.SEND_DATA_INITIAL_OK); 83 setFinalReplyCode(ReplyCodes.SEND_DATA_FINAL_OK); 84 } 85 86 /** 87 * Handle the command. Perform the following steps: 88 * <ol> 89 * <li>Invoke the {@link #beforeProcessData()} method</li> 90 * <li>Open the data connection</li> 91 * <li>Send an preliminary reply, default reply code 150</li> 92 * <li>Invoke the {@link #processData()} method</li> 93 * <li>Close the data connection</li> 94 * <li>Send the final reply, default reply code 226</li> 95 * <li>Invoke the {@link #afterProcessData()} method</li> 96 * </ol> 97 * 98 * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord) 99 */ 100 public final void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 101 102 beforeProcessData(command, session, invocationRecord); 103 104 sendPreliminaryReply(session); 105 session.openDataConnection(); 106 processData(command, session, invocationRecord); 107 session.closeDataConnection(); 108 sendFinalReply(session); 109 110 afterProcessData(command, session, invocationRecord); 111 } 112 113 /** 114 * Send the final reply. The default implementation sends a reply code of 226 with the 115 * corresponding associated reply text. 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 * 132 * @throws Exception 133 */ 134 protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 135 // Do nothing by default 136 } 137 138 /** 139 * Abstract method placeholder for subclass transfer of data across the data connection. 140 * Subclasses must override. The data connection is opened before this method and is 141 * closed after this method completes. 142 * 143 * @param command - the Command to be handled 144 * @param session - the session on which the Command was submitted 145 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 146 * handler-specific data to the InvocationRecord, as appropriate 147 * 148 * @throws Exception 149 */ 150 protected abstract void processData(Command command, Session session, InvocationRecord invocationRecord) throws Exception; 151 152 /** 153 * Perform any necessary logic after transferring data across the data connection. 154 * Do nothing by default. 155 * 156 * @param command - the Command to be handled 157 * @param session - the session on which the Command was submitted 158 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 159 * handler-specific data to the InvocationRecord, as appropriate 160 * @throws Exception 161 */ 162 protected void afterProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 163 // Do nothing by default 164 } 165 166 /** 167 * Send the preliminary reply for this command on the control connection. 168 * 169 * @param session - the Session 170 */ 171 private void sendPreliminaryReply(Session session) { 172 sendReply(session, preliminaryReplyCode, preliminaryReplyMessageKey, preliminaryReplyText, null); 173 } 174 175 /** 176 * Set the completion reply code sent after data transfer 177 * @param finalReplyCode - the final reply code 178 * 179 * @throws AssertFailedException - if the finalReplyCode is invalid 180 */ 181 public void setFinalReplyCode(int finalReplyCode) { 182 assertValidReplyCode(finalReplyCode); 183 this.finalReplyCode = finalReplyCode; 184 } 185 186 /** 187 * Set the message key for the completion reply text sent after data transfer 188 * @param finalReplyMessageKey - the final reply message key 189 */ 190 public void setFinalReplyMessageKey(String finalReplyMessageKey) { 191 this.finalReplyMessageKey = finalReplyMessageKey; 192 } 193 194 /** 195 * Set the text of the completion reply sent after data transfer 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 * @param preliminaryReplyCode - the preliminary reply code to set 205 * 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 * @param preliminaryReplyMessageKey - the preliminary reply message key 216 */ 217 public void setPreliminaryReplyMessageKey(String preliminaryReplyMessageKey) { 218 this.preliminaryReplyMessageKey = preliminaryReplyMessageKey; 219 } 220 221 /** 222 * Set the text of the completion reply sent before data transfer 223 * @param preliminaryReplyText - the preliminary reply text 224 */ 225 public void setPreliminaryReplyText(String preliminaryReplyText) { 226 this.preliminaryReplyText = preliminaryReplyText; 227 } 228 229} 230