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