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