185efb15529d45e32fea8de03c38a968c157c8262chrismair/*
285efb15529d45e32fea8de03c38a968c157c8262chrismair * Copyright 2007 the original author or authors.
385efb15529d45e32fea8de03c38a968c157c8262chrismair *
485efb15529d45e32fea8de03c38a968c157c8262chrismair * Licensed under the Apache License, Version 2.0 (the "License");
585efb15529d45e32fea8de03c38a968c157c8262chrismair * you may not use this file except in compliance with the License.
685efb15529d45e32fea8de03c38a968c157c8262chrismair * You may obtain a copy of the License at
785efb15529d45e32fea8de03c38a968c157c8262chrismair *
885efb15529d45e32fea8de03c38a968c157c8262chrismair *      http://www.apache.org/licenses/LICENSE-2.0
985efb15529d45e32fea8de03c38a968c157c8262chrismair *
1085efb15529d45e32fea8de03c38a968c157c8262chrismair * Unless required by applicable law or agreed to in writing, software
1185efb15529d45e32fea8de03c38a968c157c8262chrismair * distributed under the License is distributed on an "AS IS" BASIS,
1285efb15529d45e32fea8de03c38a968c157c8262chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1385efb15529d45e32fea8de03c38a968c157c8262chrismair * See the License for the specific language governing permissions and
1485efb15529d45e32fea8de03c38a968c157c8262chrismair * limitations under the License.
1585efb15529d45e32fea8de03c38a968c157c8262chrismair */
1685efb15529d45e32fea8de03c38a968c157c8262chrismairpackage org.mockftpserver.stub.command;
1785efb15529d45e32fea8de03c38a968c157c8262chrismair
1885efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.command.AbstractCommandHandler;
1985efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.command.Command;
2085efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.command.CommandHandler;
2185efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.command.InvocationRecord;
2285efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.command.ReplyCodes;
2385efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.session.Session;
2485efb15529d45e32fea8de03c38a968c157c8262chrismairimport org.mockftpserver.core.util.AssertFailedException;
2585efb15529d45e32fea8de03c38a968c157c8262chrismair
2685efb15529d45e32fea8de03c38a968c157c8262chrismair/**
2785efb15529d45e32fea8de03c38a968c157c8262chrismair * Abstract superclass for CommandHandlers that read from or write to the data connection.
2885efb15529d45e32fea8de03c38a968c157c8262chrismair * <p>
2985efb15529d45e32fea8de03c38a968c157c8262chrismair * Return two replies on the control connection: by default a reply code of 150 before the
3085efb15529d45e32fea8de03c38a968c157c8262chrismair * data transfer across the data connection and another reply of 226 after the data transfer
3185efb15529d45e32fea8de03c38a968c157c8262chrismair * is complete.
3285efb15529d45e32fea8de03c38a968c157c8262chrismair * <p>
3385efb15529d45e32fea8de03c38a968c157c8262chrismair * This class implements the <i>Template Method</i> pattern. Subclasses must implement the abstract
3485efb15529d45e32fea8de03c38a968c157c8262chrismair * {@link #processData()} method to perform read or writes across the data connection.
3585efb15529d45e32fea8de03c38a968c157c8262chrismair * <p>
3685efb15529d45e32fea8de03c38a968c157c8262chrismair * Subclasses can optionally override the {@link #beforeProcessData(Command, Session, InvocationRecord)}
3785efb15529d45e32fea8de03c38a968c157c8262chrismair * method for logic before the data transfer or the {@link #afterProcessData(Command, Session, InvocationRecord)}
3885efb15529d45e32fea8de03c38a968c157c8262chrismair * method for logic after the data transfer.
3985efb15529d45e32fea8de03c38a968c157c8262chrismair * <p>
4085efb15529d45e32fea8de03c38a968c157c8262chrismair * Subclasses can optionally override the reply code and/or text for the initial reply (before
4185efb15529d45e32fea8de03c38a968c157c8262chrismair * the data transfer across the data connection) by calling {@link #setPreliminaryReplyCode(int)},
4285efb15529d45e32fea8de03c38a968c157c8262chrismair * {@link #setPreliminaryReplyMessageKey(String)} and/or {@link #setPreliminaryReplyText(String)}
4385efb15529d45e32fea8de03c38a968c157c8262chrismair * methods.
4485efb15529d45e32fea8de03c38a968c157c8262chrismair * <p>
4585efb15529d45e32fea8de03c38a968c157c8262chrismair * Subclasses can optionally override the reply code and/or text for the final reply (after the
4685efb15529d45e32fea8de03c38a968c157c8262chrismair * the data transfer is complete) by calling {@link #setFinalReplyCode(int)},
4785efb15529d45e32fea8de03c38a968c157c8262chrismair * {@link #setFinalReplyMessageKey(String)} and/or {@link #setFinalReplyText(String)} methods.
4885efb15529d45e32fea8de03c38a968c157c8262chrismair *
4985efb15529d45e32fea8de03c38a968c157c8262chrismair * @version $Revision$ - $Date$
5085efb15529d45e32fea8de03c38a968c157c8262chrismair *
5185efb15529d45e32fea8de03c38a968c157c8262chrismair * @author Chris Mair
5285efb15529d45e32fea8de03c38a968c157c8262chrismair */
5385efb15529d45e32fea8de03c38a968c157c8262chrismairpublic abstract class AbstractStubDataCommandHandler extends AbstractCommandHandler implements CommandHandler {
5485efb15529d45e32fea8de03c38a968c157c8262chrismair
5585efb15529d45e32fea8de03c38a968c157c8262chrismair    // The completion reply code sent before the data transfer
5685efb15529d45e32fea8de03c38a968c157c8262chrismair    protected int preliminaryReplyCode = 0;
5785efb15529d45e32fea8de03c38a968c157c8262chrismair
5885efb15529d45e32fea8de03c38a968c157c8262chrismair    // The text for the preliminary reply. If null, use the default message associated with the reply code.
5985efb15529d45e32fea8de03c38a968c157c8262chrismair    // If not null, this value overrides the preliminaryReplyMessageKey - i.e., this text is used instead of
6085efb15529d45e32fea8de03c38a968c157c8262chrismair    // a localized message.
6185efb15529d45e32fea8de03c38a968c157c8262chrismair    protected String preliminaryReplyText = null;
6285efb15529d45e32fea8de03c38a968c157c8262chrismair
6385efb15529d45e32fea8de03c38a968c157c8262chrismair    // The message key for the preliminary reply text. If null, use the default message associated with
6485efb15529d45e32fea8de03c38a968c157c8262chrismair    // the reply code.
6585efb15529d45e32fea8de03c38a968c157c8262chrismair    protected String preliminaryReplyMessageKey = null;
6685efb15529d45e32fea8de03c38a968c157c8262chrismair
6785efb15529d45e32fea8de03c38a968c157c8262chrismair    // The completion reply code sent after data transfer
6885efb15529d45e32fea8de03c38a968c157c8262chrismair    protected int finalReplyCode = 0;
6985efb15529d45e32fea8de03c38a968c157c8262chrismair
7085efb15529d45e32fea8de03c38a968c157c8262chrismair    // The text for the completion reply. If null, use the default message associated with the reply code.
7185efb15529d45e32fea8de03c38a968c157c8262chrismair    // If not null, this value overrides the finalReplyMessageKey - i.e., this text is used instead of
7285efb15529d45e32fea8de03c38a968c157c8262chrismair    // a localized message.
7385efb15529d45e32fea8de03c38a968c157c8262chrismair    protected String finalReplyText = null;
7485efb15529d45e32fea8de03c38a968c157c8262chrismair
7585efb15529d45e32fea8de03c38a968c157c8262chrismair    // The message key for the completion reply text. If null, use the default message associated with the reply code
7685efb15529d45e32fea8de03c38a968c157c8262chrismair    protected String finalReplyMessageKey = null;
7785efb15529d45e32fea8de03c38a968c157c8262chrismair
7885efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
7985efb15529d45e32fea8de03c38a968c157c8262chrismair     * Constructor. Initialize the preliminary and final reply code.
8085efb15529d45e32fea8de03c38a968c157c8262chrismair     */
8185efb15529d45e32fea8de03c38a968c157c8262chrismair    protected AbstractStubDataCommandHandler() {
8285efb15529d45e32fea8de03c38a968c157c8262chrismair        setPreliminaryReplyCode(ReplyCodes.SEND_DATA_INITIAL_OK);
8385efb15529d45e32fea8de03c38a968c157c8262chrismair        setFinalReplyCode(ReplyCodes.SEND_DATA_FINAL_OK);
8485efb15529d45e32fea8de03c38a968c157c8262chrismair    }
8585efb15529d45e32fea8de03c38a968c157c8262chrismair
8685efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
8785efb15529d45e32fea8de03c38a968c157c8262chrismair     * Handle the command. Perform the following steps:
8885efb15529d45e32fea8de03c38a968c157c8262chrismair     * <ol>
8985efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Invoke the {@link #beforeProcessData()} method</li>
9085efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Open the data connection</li>
9185efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Send an preliminary reply, default reply code 150</li>
9285efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Invoke the {@link #processData()} method</li>
9385efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Close the data connection</li>
9485efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Send the final reply, default reply code 226</li>
9585efb15529d45e32fea8de03c38a968c157c8262chrismair     * <li>Invoke the {@link #afterProcessData()} method</li>
9685efb15529d45e32fea8de03c38a968c157c8262chrismair     * </ol>
9785efb15529d45e32fea8de03c38a968c157c8262chrismair     *
9885efb15529d45e32fea8de03c38a968c157c8262chrismair     * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
9985efb15529d45e32fea8de03c38a968c157c8262chrismair     */
10085efb15529d45e32fea8de03c38a968c157c8262chrismair    public final void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
10185efb15529d45e32fea8de03c38a968c157c8262chrismair
10285efb15529d45e32fea8de03c38a968c157c8262chrismair        beforeProcessData(command, session, invocationRecord);
10385efb15529d45e32fea8de03c38a968c157c8262chrismair
10485efb15529d45e32fea8de03c38a968c157c8262chrismair        sendPreliminaryReply(session);
10585efb15529d45e32fea8de03c38a968c157c8262chrismair        session.openDataConnection();
10685efb15529d45e32fea8de03c38a968c157c8262chrismair        processData(command, session, invocationRecord);
10785efb15529d45e32fea8de03c38a968c157c8262chrismair        session.closeDataConnection();
10885efb15529d45e32fea8de03c38a968c157c8262chrismair        sendFinalReply(session);
10985efb15529d45e32fea8de03c38a968c157c8262chrismair
11085efb15529d45e32fea8de03c38a968c157c8262chrismair        afterProcessData(command, session, invocationRecord);
11185efb15529d45e32fea8de03c38a968c157c8262chrismair    }
11285efb15529d45e32fea8de03c38a968c157c8262chrismair
11385efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
11485efb15529d45e32fea8de03c38a968c157c8262chrismair     * Send the final reply. The default implementation sends a reply code of 226 with the
11585efb15529d45e32fea8de03c38a968c157c8262chrismair     * corresponding associated reply text.
11685efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param session - the Session
11785efb15529d45e32fea8de03c38a968c157c8262chrismair     */
11885efb15529d45e32fea8de03c38a968c157c8262chrismair    protected void sendFinalReply(Session session) {
11985efb15529d45e32fea8de03c38a968c157c8262chrismair        sendReply(session, finalReplyCode, finalReplyMessageKey, finalReplyText, null);
12085efb15529d45e32fea8de03c38a968c157c8262chrismair    }
12185efb15529d45e32fea8de03c38a968c157c8262chrismair
12285efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
12385efb15529d45e32fea8de03c38a968c157c8262chrismair     * Perform any necessary logic before transferring data across the data connection.
12485efb15529d45e32fea8de03c38a968c157c8262chrismair     * Do nothing by default. Subclasses should override to validate command parameters and
12585efb15529d45e32fea8de03c38a968c157c8262chrismair     * store information in the InvocationRecord.
12685efb15529d45e32fea8de03c38a968c157c8262chrismair     *
12785efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param command - the Command to be handled
12885efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param session - the session on which the Command was submitted
12985efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
13085efb15529d45e32fea8de03c38a968c157c8262chrismair     *      handler-specific data to the InvocationRecord, as appropriate
13185efb15529d45e32fea8de03c38a968c157c8262chrismair     *
13285efb15529d45e32fea8de03c38a968c157c8262chrismair     * @throws Exception
13385efb15529d45e32fea8de03c38a968c157c8262chrismair     */
13485efb15529d45e32fea8de03c38a968c157c8262chrismair    protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
13585efb15529d45e32fea8de03c38a968c157c8262chrismair        // Do nothing by default
13685efb15529d45e32fea8de03c38a968c157c8262chrismair    }
13785efb15529d45e32fea8de03c38a968c157c8262chrismair
13885efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
13985efb15529d45e32fea8de03c38a968c157c8262chrismair     * Abstract method placeholder for subclass transfer of data across the data connection.
14085efb15529d45e32fea8de03c38a968c157c8262chrismair     * Subclasses must override. The data connection is opened before this method and is
14185efb15529d45e32fea8de03c38a968c157c8262chrismair     * closed after this method completes.
14285efb15529d45e32fea8de03c38a968c157c8262chrismair     *
14385efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param command - the Command to be handled
14485efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param session - the session on which the Command was submitted
14585efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
14685efb15529d45e32fea8de03c38a968c157c8262chrismair     *      handler-specific data to the InvocationRecord, as appropriate
14785efb15529d45e32fea8de03c38a968c157c8262chrismair     *
14885efb15529d45e32fea8de03c38a968c157c8262chrismair     * @throws Exception
14985efb15529d45e32fea8de03c38a968c157c8262chrismair     */
15085efb15529d45e32fea8de03c38a968c157c8262chrismair    protected abstract void processData(Command command, Session session, InvocationRecord invocationRecord) throws Exception;
15185efb15529d45e32fea8de03c38a968c157c8262chrismair
15285efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
15385efb15529d45e32fea8de03c38a968c157c8262chrismair     * Perform any necessary logic after transferring data across the data connection.
15485efb15529d45e32fea8de03c38a968c157c8262chrismair     * Do nothing by default.
15585efb15529d45e32fea8de03c38a968c157c8262chrismair     *
15685efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param command - the Command to be handled
15785efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param session - the session on which the Command was submitted
15885efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
15985efb15529d45e32fea8de03c38a968c157c8262chrismair     *      handler-specific data to the InvocationRecord, as appropriate
16085efb15529d45e32fea8de03c38a968c157c8262chrismair     * @throws Exception
16185efb15529d45e32fea8de03c38a968c157c8262chrismair     */
16285efb15529d45e32fea8de03c38a968c157c8262chrismair    protected void afterProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
16385efb15529d45e32fea8de03c38a968c157c8262chrismair       // Do nothing by default
16485efb15529d45e32fea8de03c38a968c157c8262chrismair    }
16585efb15529d45e32fea8de03c38a968c157c8262chrismair
16685efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
16785efb15529d45e32fea8de03c38a968c157c8262chrismair     * Send the preliminary reply for this command on the control connection.
16885efb15529d45e32fea8de03c38a968c157c8262chrismair     *
16985efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param session - the Session
17085efb15529d45e32fea8de03c38a968c157c8262chrismair     */
17185efb15529d45e32fea8de03c38a968c157c8262chrismair    private void sendPreliminaryReply(Session session) {
17285efb15529d45e32fea8de03c38a968c157c8262chrismair        sendReply(session, preliminaryReplyCode, preliminaryReplyMessageKey, preliminaryReplyText, null);
17385efb15529d45e32fea8de03c38a968c157c8262chrismair    }
17485efb15529d45e32fea8de03c38a968c157c8262chrismair
17585efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
17685efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the completion reply code sent after data transfer
17785efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param finalReplyCode - the final reply code
17885efb15529d45e32fea8de03c38a968c157c8262chrismair     *
17985efb15529d45e32fea8de03c38a968c157c8262chrismair     * @throws AssertFailedException - if the finalReplyCode is invalid
18085efb15529d45e32fea8de03c38a968c157c8262chrismair     */
18185efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setFinalReplyCode(int finalReplyCode) {
18285efb15529d45e32fea8de03c38a968c157c8262chrismair        assertValidReplyCode(finalReplyCode);
18385efb15529d45e32fea8de03c38a968c157c8262chrismair        this.finalReplyCode = finalReplyCode;
18485efb15529d45e32fea8de03c38a968c157c8262chrismair    }
18585efb15529d45e32fea8de03c38a968c157c8262chrismair
18685efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
18785efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the message key for the completion reply text sent after data transfer
18885efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param finalReplyMessageKey - the final reply message key
18985efb15529d45e32fea8de03c38a968c157c8262chrismair     */
19085efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setFinalReplyMessageKey(String finalReplyMessageKey) {
19185efb15529d45e32fea8de03c38a968c157c8262chrismair        this.finalReplyMessageKey = finalReplyMessageKey;
19285efb15529d45e32fea8de03c38a968c157c8262chrismair    }
19385efb15529d45e32fea8de03c38a968c157c8262chrismair
19485efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
19585efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the text of the completion reply sent after data transfer
19685efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param finalReplyText - the final reply text
19785efb15529d45e32fea8de03c38a968c157c8262chrismair     */
19885efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setFinalReplyText(String finalReplyText) {
19985efb15529d45e32fea8de03c38a968c157c8262chrismair        this.finalReplyText = finalReplyText;
20085efb15529d45e32fea8de03c38a968c157c8262chrismair    }
20185efb15529d45e32fea8de03c38a968c157c8262chrismair
20285efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
20385efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the completion reply code sent before data transfer
20485efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param preliminaryReplyCode - the preliminary reply code to set
20585efb15529d45e32fea8de03c38a968c157c8262chrismair     *
20685efb15529d45e32fea8de03c38a968c157c8262chrismair     * @throws AssertFailedException - if the preliminaryReplyCode is invalid
20785efb15529d45e32fea8de03c38a968c157c8262chrismair     */
20885efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setPreliminaryReplyCode(int preliminaryReplyCode) {
20985efb15529d45e32fea8de03c38a968c157c8262chrismair        assertValidReplyCode(preliminaryReplyCode);
21085efb15529d45e32fea8de03c38a968c157c8262chrismair        this.preliminaryReplyCode = preliminaryReplyCode;
21185efb15529d45e32fea8de03c38a968c157c8262chrismair    }
21285efb15529d45e32fea8de03c38a968c157c8262chrismair
21385efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
21485efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the message key for the completion reply text sent before data transfer
21585efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param preliminaryReplyMessageKey - the preliminary reply message key
21685efb15529d45e32fea8de03c38a968c157c8262chrismair     */
21785efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setPreliminaryReplyMessageKey(String preliminaryReplyMessageKey) {
21885efb15529d45e32fea8de03c38a968c157c8262chrismair        this.preliminaryReplyMessageKey = preliminaryReplyMessageKey;
21985efb15529d45e32fea8de03c38a968c157c8262chrismair    }
22085efb15529d45e32fea8de03c38a968c157c8262chrismair
22185efb15529d45e32fea8de03c38a968c157c8262chrismair    /**
22285efb15529d45e32fea8de03c38a968c157c8262chrismair     * Set the text of the completion reply sent before data transfer
22385efb15529d45e32fea8de03c38a968c157c8262chrismair     * @param preliminaryReplyText - the preliminary reply text
22485efb15529d45e32fea8de03c38a968c157c8262chrismair     */
22585efb15529d45e32fea8de03c38a968c157c8262chrismair    public void setPreliminaryReplyText(String preliminaryReplyText) {
22685efb15529d45e32fea8de03c38a968c157c8262chrismair        this.preliminaryReplyText = preliminaryReplyText;
22785efb15529d45e32fea8de03c38a968c157c8262chrismair    }
22885efb15529d45e32fea8de03c38a968c157c8262chrismair
22985efb15529d45e32fea8de03c38a968c157c8262chrismair}
230