14531116f8e675a208710e987bfe3b58faeb12db2chrismair/* 24531116f8e675a208710e987bfe3b58faeb12db2chrismair * Copyright 2007 the original author or authors. 34531116f8e675a208710e987bfe3b58faeb12db2chrismair * 44531116f8e675a208710e987bfe3b58faeb12db2chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 54531116f8e675a208710e987bfe3b58faeb12db2chrismair * you may not use this file except in compliance with the License. 64531116f8e675a208710e987bfe3b58faeb12db2chrismair * You may obtain a copy of the License at 74531116f8e675a208710e987bfe3b58faeb12db2chrismair * 84531116f8e675a208710e987bfe3b58faeb12db2chrismair * http://www.apache.org/licenses/LICENSE-2.0 94531116f8e675a208710e987bfe3b58faeb12db2chrismair * 104531116f8e675a208710e987bfe3b58faeb12db2chrismair * Unless required by applicable law or agreed to in writing, software 114531116f8e675a208710e987bfe3b58faeb12db2chrismair * distributed under the License is distributed on an "AS IS" BASIS, 124531116f8e675a208710e987bfe3b58faeb12db2chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134531116f8e675a208710e987bfe3b58faeb12db2chrismair * See the License for the specific language governing permissions and 144531116f8e675a208710e987bfe3b58faeb12db2chrismair * limitations under the License. 154531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 164531116f8e675a208710e987bfe3b58faeb12db2chrismairpackage org.mockftpserver.stub.command; 174531116f8e675a208710e987bfe3b58faeb12db2chrismair 184531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.command.AbstractCommandHandler; 194531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.command.Command; 204531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.command.CommandHandler; 214531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.command.InvocationRecord; 224531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.command.ReplyCodes; 234531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.session.Session; 244531116f8e675a208710e987bfe3b58faeb12db2chrismairimport org.mockftpserver.core.util.AssertFailedException; 254531116f8e675a208710e987bfe3b58faeb12db2chrismair 264531116f8e675a208710e987bfe3b58faeb12db2chrismair/** 274531116f8e675a208710e987bfe3b58faeb12db2chrismair * Abstract superclass for CommandHandlers that read from or write to the data connection. 284531116f8e675a208710e987bfe3b58faeb12db2chrismair * <p> 294531116f8e675a208710e987bfe3b58faeb12db2chrismair * Return two replies on the control connection: by default a reply code of 150 before the 304531116f8e675a208710e987bfe3b58faeb12db2chrismair * data transfer across the data connection and another reply of 226 after the data transfer 314531116f8e675a208710e987bfe3b58faeb12db2chrismair * is complete. 324531116f8e675a208710e987bfe3b58faeb12db2chrismair * <p> 334531116f8e675a208710e987bfe3b58faeb12db2chrismair * This class implements the <i>Template Method</i> pattern. Subclasses must implement the abstract 344531116f8e675a208710e987bfe3b58faeb12db2chrismair * {@link #processData()} method to perform read or writes across the data connection. 354531116f8e675a208710e987bfe3b58faeb12db2chrismair * <p> 364531116f8e675a208710e987bfe3b58faeb12db2chrismair * Subclasses can optionally override the {@link #beforeProcessData(Command, Session, InvocationRecord)} 374531116f8e675a208710e987bfe3b58faeb12db2chrismair * method for logic before the data transfer or the {@link #afterProcessData(Command, Session, InvocationRecord)} 384531116f8e675a208710e987bfe3b58faeb12db2chrismair * method for logic after the data transfer. 394531116f8e675a208710e987bfe3b58faeb12db2chrismair * <p> 404531116f8e675a208710e987bfe3b58faeb12db2chrismair * Subclasses can optionally override the reply code and/or text for the initial reply (before 414531116f8e675a208710e987bfe3b58faeb12db2chrismair * the data transfer across the data connection) by calling {@link #setPreliminaryReplyCode(int)}, 424531116f8e675a208710e987bfe3b58faeb12db2chrismair * {@link #setPreliminaryReplyMessageKey(String)} and/or {@link #setPreliminaryReplyText(String)} 434531116f8e675a208710e987bfe3b58faeb12db2chrismair * methods. 444531116f8e675a208710e987bfe3b58faeb12db2chrismair * <p> 454531116f8e675a208710e987bfe3b58faeb12db2chrismair * Subclasses can optionally override the reply code and/or text for the final reply (after the 464531116f8e675a208710e987bfe3b58faeb12db2chrismair * the data transfer is complete) by calling {@link #setFinalReplyCode(int)}, 474531116f8e675a208710e987bfe3b58faeb12db2chrismair * {@link #setFinalReplyMessageKey(String)} and/or {@link #setFinalReplyText(String)} methods. 484531116f8e675a208710e987bfe3b58faeb12db2chrismair * 494531116f8e675a208710e987bfe3b58faeb12db2chrismair * @version $Revision$ - $Date$ 504531116f8e675a208710e987bfe3b58faeb12db2chrismair * 514531116f8e675a208710e987bfe3b58faeb12db2chrismair * @author Chris Mair 524531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 534531116f8e675a208710e987bfe3b58faeb12db2chrismairpublic abstract class AbstractStubDataCommandHandler extends AbstractCommandHandler implements CommandHandler { 544531116f8e675a208710e987bfe3b58faeb12db2chrismair 554531116f8e675a208710e987bfe3b58faeb12db2chrismair // The completion reply code sent before the data transfer 564531116f8e675a208710e987bfe3b58faeb12db2chrismair protected int preliminaryReplyCode = 0; 574531116f8e675a208710e987bfe3b58faeb12db2chrismair 584531116f8e675a208710e987bfe3b58faeb12db2chrismair // The text for the preliminary reply. If null, use the default message associated with the reply code. 594531116f8e675a208710e987bfe3b58faeb12db2chrismair // If not null, this value overrides the preliminaryReplyMessageKey - i.e., this text is used instead of 604531116f8e675a208710e987bfe3b58faeb12db2chrismair // a localized message. 614531116f8e675a208710e987bfe3b58faeb12db2chrismair protected String preliminaryReplyText = null; 624531116f8e675a208710e987bfe3b58faeb12db2chrismair 634531116f8e675a208710e987bfe3b58faeb12db2chrismair // The message key for the preliminary reply text. If null, use the default message associated with 644531116f8e675a208710e987bfe3b58faeb12db2chrismair // the reply code. 654531116f8e675a208710e987bfe3b58faeb12db2chrismair protected String preliminaryReplyMessageKey = null; 664531116f8e675a208710e987bfe3b58faeb12db2chrismair 674531116f8e675a208710e987bfe3b58faeb12db2chrismair // The completion reply code sent after data transfer 684531116f8e675a208710e987bfe3b58faeb12db2chrismair protected int finalReplyCode = 0; 694531116f8e675a208710e987bfe3b58faeb12db2chrismair 704531116f8e675a208710e987bfe3b58faeb12db2chrismair // The text for the completion reply. If null, use the default message associated with the reply code. 714531116f8e675a208710e987bfe3b58faeb12db2chrismair // If not null, this value overrides the finalReplyMessageKey - i.e., this text is used instead of 724531116f8e675a208710e987bfe3b58faeb12db2chrismair // a localized message. 734531116f8e675a208710e987bfe3b58faeb12db2chrismair protected String finalReplyText = null; 744531116f8e675a208710e987bfe3b58faeb12db2chrismair 754531116f8e675a208710e987bfe3b58faeb12db2chrismair // The message key for the completion reply text. If null, use the default message associated with the reply code 764531116f8e675a208710e987bfe3b58faeb12db2chrismair protected String finalReplyMessageKey = null; 774531116f8e675a208710e987bfe3b58faeb12db2chrismair 784531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 794531116f8e675a208710e987bfe3b58faeb12db2chrismair * Constructor. Initialize the preliminary and final reply code. 804531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 814531116f8e675a208710e987bfe3b58faeb12db2chrismair protected AbstractStubDataCommandHandler() { 824531116f8e675a208710e987bfe3b58faeb12db2chrismair setPreliminaryReplyCode(ReplyCodes.SEND_DATA_INITIAL_OK); 834531116f8e675a208710e987bfe3b58faeb12db2chrismair setFinalReplyCode(ReplyCodes.SEND_DATA_FINAL_OK); 844531116f8e675a208710e987bfe3b58faeb12db2chrismair } 854531116f8e675a208710e987bfe3b58faeb12db2chrismair 864531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 874531116f8e675a208710e987bfe3b58faeb12db2chrismair * Handle the command. Perform the following steps: 884531116f8e675a208710e987bfe3b58faeb12db2chrismair * <ol> 894531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Invoke the {@link #beforeProcessData()} method</li> 904531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Open the data connection</li> 914531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Send an preliminary reply, default reply code 150</li> 924531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Invoke the {@link #processData()} method</li> 934531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Close the data connection</li> 944531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Send the final reply, default reply code 226</li> 954531116f8e675a208710e987bfe3b58faeb12db2chrismair * <li>Invoke the {@link #afterProcessData()} method</li> 964531116f8e675a208710e987bfe3b58faeb12db2chrismair * </ol> 974531116f8e675a208710e987bfe3b58faeb12db2chrismair * 984531116f8e675a208710e987bfe3b58faeb12db2chrismair * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord) 994531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1004531116f8e675a208710e987bfe3b58faeb12db2chrismair public final void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 1014531116f8e675a208710e987bfe3b58faeb12db2chrismair 1024531116f8e675a208710e987bfe3b58faeb12db2chrismair beforeProcessData(command, session, invocationRecord); 1034531116f8e675a208710e987bfe3b58faeb12db2chrismair 1044531116f8e675a208710e987bfe3b58faeb12db2chrismair sendPreliminaryReply(session); 1054531116f8e675a208710e987bfe3b58faeb12db2chrismair session.openDataConnection(); 1064531116f8e675a208710e987bfe3b58faeb12db2chrismair processData(command, session, invocationRecord); 1074531116f8e675a208710e987bfe3b58faeb12db2chrismair session.closeDataConnection(); 1084531116f8e675a208710e987bfe3b58faeb12db2chrismair sendFinalReply(session); 1094531116f8e675a208710e987bfe3b58faeb12db2chrismair 1104531116f8e675a208710e987bfe3b58faeb12db2chrismair afterProcessData(command, session, invocationRecord); 1114531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1124531116f8e675a208710e987bfe3b58faeb12db2chrismair 1134531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1144531116f8e675a208710e987bfe3b58faeb12db2chrismair * Send the final reply. The default implementation sends a reply code of 226 with the 1154531116f8e675a208710e987bfe3b58faeb12db2chrismair * corresponding associated reply text. 1164531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param session - the Session 1174531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1184531116f8e675a208710e987bfe3b58faeb12db2chrismair protected void sendFinalReply(Session session) { 1194531116f8e675a208710e987bfe3b58faeb12db2chrismair sendReply(session, finalReplyCode, finalReplyMessageKey, finalReplyText, null); 1204531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1214531116f8e675a208710e987bfe3b58faeb12db2chrismair 1224531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1234531116f8e675a208710e987bfe3b58faeb12db2chrismair * Perform any necessary logic before transferring data across the data connection. 1244531116f8e675a208710e987bfe3b58faeb12db2chrismair * Do nothing by default. Subclasses should override to validate command parameters and 1254531116f8e675a208710e987bfe3b58faeb12db2chrismair * store information in the InvocationRecord. 1264531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1274531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param command - the Command to be handled 1284531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param session - the session on which the Command was submitted 1294531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 1304531116f8e675a208710e987bfe3b58faeb12db2chrismair * handler-specific data to the InvocationRecord, as appropriate 1314531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1324531116f8e675a208710e987bfe3b58faeb12db2chrismair * @throws Exception 1334531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1344531116f8e675a208710e987bfe3b58faeb12db2chrismair protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 1354531116f8e675a208710e987bfe3b58faeb12db2chrismair // Do nothing by default 1364531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1374531116f8e675a208710e987bfe3b58faeb12db2chrismair 1384531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1394531116f8e675a208710e987bfe3b58faeb12db2chrismair * Abstract method placeholder for subclass transfer of data across the data connection. 1404531116f8e675a208710e987bfe3b58faeb12db2chrismair * Subclasses must override. The data connection is opened before this method and is 1414531116f8e675a208710e987bfe3b58faeb12db2chrismair * closed after this method completes. 1424531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1434531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param command - the Command to be handled 1444531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param session - the session on which the Command was submitted 1454531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 1464531116f8e675a208710e987bfe3b58faeb12db2chrismair * handler-specific data to the InvocationRecord, as appropriate 1474531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1484531116f8e675a208710e987bfe3b58faeb12db2chrismair * @throws Exception 1494531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1504531116f8e675a208710e987bfe3b58faeb12db2chrismair protected abstract void processData(Command command, Session session, InvocationRecord invocationRecord) throws Exception; 1514531116f8e675a208710e987bfe3b58faeb12db2chrismair 1524531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1534531116f8e675a208710e987bfe3b58faeb12db2chrismair * Perform any necessary logic after transferring data across the data connection. 1544531116f8e675a208710e987bfe3b58faeb12db2chrismair * Do nothing by default. 1554531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1564531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param command - the Command to be handled 1574531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param session - the session on which the Command was submitted 1584531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add 1594531116f8e675a208710e987bfe3b58faeb12db2chrismair * handler-specific data to the InvocationRecord, as appropriate 1604531116f8e675a208710e987bfe3b58faeb12db2chrismair * @throws Exception 1614531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1624531116f8e675a208710e987bfe3b58faeb12db2chrismair protected void afterProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception { 1634531116f8e675a208710e987bfe3b58faeb12db2chrismair // Do nothing by default 1644531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1654531116f8e675a208710e987bfe3b58faeb12db2chrismair 1664531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1674531116f8e675a208710e987bfe3b58faeb12db2chrismair * Send the preliminary reply for this command on the control connection. 1684531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1694531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param session - the Session 1704531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1714531116f8e675a208710e987bfe3b58faeb12db2chrismair private void sendPreliminaryReply(Session session) { 1724531116f8e675a208710e987bfe3b58faeb12db2chrismair sendReply(session, preliminaryReplyCode, preliminaryReplyMessageKey, preliminaryReplyText, null); 1734531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1744531116f8e675a208710e987bfe3b58faeb12db2chrismair 1754531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1764531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the completion reply code sent after data transfer 1774531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param finalReplyCode - the final reply code 1784531116f8e675a208710e987bfe3b58faeb12db2chrismair * 1794531116f8e675a208710e987bfe3b58faeb12db2chrismair * @throws AssertFailedException - if the finalReplyCode is invalid 1804531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1814531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setFinalReplyCode(int finalReplyCode) { 1824531116f8e675a208710e987bfe3b58faeb12db2chrismair assertValidReplyCode(finalReplyCode); 1834531116f8e675a208710e987bfe3b58faeb12db2chrismair this.finalReplyCode = finalReplyCode; 1844531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1854531116f8e675a208710e987bfe3b58faeb12db2chrismair 1864531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1874531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the message key for the completion reply text sent after data transfer 1884531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param finalReplyMessageKey - the final reply message key 1894531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1904531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setFinalReplyMessageKey(String finalReplyMessageKey) { 1914531116f8e675a208710e987bfe3b58faeb12db2chrismair this.finalReplyMessageKey = finalReplyMessageKey; 1924531116f8e675a208710e987bfe3b58faeb12db2chrismair } 1934531116f8e675a208710e987bfe3b58faeb12db2chrismair 1944531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 1954531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the text of the completion reply sent after data transfer 1964531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param finalReplyText - the final reply text 1974531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 1984531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setFinalReplyText(String finalReplyText) { 1994531116f8e675a208710e987bfe3b58faeb12db2chrismair this.finalReplyText = finalReplyText; 2004531116f8e675a208710e987bfe3b58faeb12db2chrismair } 2014531116f8e675a208710e987bfe3b58faeb12db2chrismair 2024531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 2034531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the completion reply code sent before data transfer 2044531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param preliminaryReplyCode - the preliminary reply code to set 2054531116f8e675a208710e987bfe3b58faeb12db2chrismair * 2064531116f8e675a208710e987bfe3b58faeb12db2chrismair * @throws AssertFailedException - if the preliminaryReplyCode is invalid 2074531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 2084531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setPreliminaryReplyCode(int preliminaryReplyCode) { 2094531116f8e675a208710e987bfe3b58faeb12db2chrismair assertValidReplyCode(preliminaryReplyCode); 2104531116f8e675a208710e987bfe3b58faeb12db2chrismair this.preliminaryReplyCode = preliminaryReplyCode; 2114531116f8e675a208710e987bfe3b58faeb12db2chrismair } 2124531116f8e675a208710e987bfe3b58faeb12db2chrismair 2134531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 2144531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the message key for the completion reply text sent before data transfer 2154531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param preliminaryReplyMessageKey - the preliminary reply message key 2164531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 2174531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setPreliminaryReplyMessageKey(String preliminaryReplyMessageKey) { 2184531116f8e675a208710e987bfe3b58faeb12db2chrismair this.preliminaryReplyMessageKey = preliminaryReplyMessageKey; 2194531116f8e675a208710e987bfe3b58faeb12db2chrismair } 2204531116f8e675a208710e987bfe3b58faeb12db2chrismair 2214531116f8e675a208710e987bfe3b58faeb12db2chrismair /** 2224531116f8e675a208710e987bfe3b58faeb12db2chrismair * Set the text of the completion reply sent before data transfer 2234531116f8e675a208710e987bfe3b58faeb12db2chrismair * @param preliminaryReplyText - the preliminary reply text 2244531116f8e675a208710e987bfe3b58faeb12db2chrismair */ 2254531116f8e675a208710e987bfe3b58faeb12db2chrismair public void setPreliminaryReplyText(String preliminaryReplyText) { 2264531116f8e675a208710e987bfe3b58faeb12db2chrismair this.preliminaryReplyText = preliminaryReplyText; 2274531116f8e675a208710e987bfe3b58faeb12db2chrismair } 2284531116f8e675a208710e987bfe3b58faeb12db2chrismair 2294531116f8e675a208710e987bfe3b58faeb12db2chrismair} 230