Home | History | Annotate | Download | only in command
      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