blob: bb817abc7564510cb91402aaa77e9595b975f73e [file] [log] [blame]
chrismair00dc7bd2014-05-11 21:21:28 +00001/*
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 */
16package org.mockftpserver.core.command;
17
18import org.mockftpserver.core.CommandSyntaxException;
19import org.mockftpserver.core.session.Session;
20import org.mockftpserver.core.util.Assert;
21import org.mockftpserver.core.util.AssertFailedException;
22
23import java.text.MessageFormat;
24import java.util.ArrayList;
25import java.util.List;
26import java.util.MissingResourceException;
27
28/**
29 * The abstract superclass for CommandHandler classes that manage the List of InvocationRecord
30 * objects corresponding to each invocation of the command handler, and provide helper methods for subclasses.
31 *
32 * @author Chris Mair
33 * @version $Revision$ - $Date$
34 */
35public abstract class AbstractTrackingCommandHandler extends AbstractCommandHandler implements InvocationHistory {
36
37 private List invocations = new ArrayList();
38
39 // -------------------------------------------------------------------------
40 // Template Method
41 // -------------------------------------------------------------------------
42
43 /**
44 * Handle the specified command for the session. This method is declared to throw Exception,
45 * allowing CommandHandler implementations to avoid unnecessary exception-handling. All checked
46 * exceptions are expected to be wrapped and handled by the caller.
47 *
48 * @param command - the Command to be handled
49 * @param session - the session on which the Command was submitted
50 * @throws Exception
51 * @throws AssertFailedException - if the command or session is null
52 * @see org.mockftpserver.core.command.CommandHandler#handleCommand(org.mockftpserver.core.command.Command,
53 * org.mockftpserver.core.session.Session)
54 */
55 public final void handleCommand(Command command, Session session) throws Exception {
56 Assert.notNull(command, "command");
57 Assert.notNull(session, "session");
58 InvocationRecord invocationRecord = new InvocationRecord(command, session.getClientHost());
59 invocations.add(invocationRecord);
60 try {
61 handleCommand(command, session, invocationRecord);
62 }
63 catch (CommandSyntaxException e) {
64 sendReply(session, ReplyCodes.COMMAND_SYNTAX_ERROR, null, null, null);
65 }
66 invocationRecord.lock();
67 }
68
69 /**
70 * Handle the specified command for the session. This method is declared to throw Exception,
71 * allowing CommandHandler implementations to avoid unnecessary exception-handling. All checked
72 * exceptions are expected to be wrapped and handled by the caller.
73 *
74 * @param command - the Command to be handled
75 * @param session - the session on which the Command was submitted
76 * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
77 * handler-specific data to the InvocationRecord, as appropriate
78 * @throws Exception
79 */
80 protected abstract void handleCommand(Command command, Session session, InvocationRecord invocationRecord)
81 throws Exception;
82
83 // -------------------------------------------------------------------------
84 // Utility methods for subclasses
85 // -------------------------------------------------------------------------
86
87 /**
88 * Send a reply for this command on the control connection.
89 * <p/>
90 * The reply code is designated by the <code>replyCode</code> property, and the reply text
91 * is determined by the following rules:
92 * <ol>
93 * <li>If the <code>replyText</code> property is non-null, then use that.</li>
94 * <li>Otherwise, if <code>replyMessageKey</code> is non-null, the use that to retrieve a
95 * localized message from the <code>replyText</code> ResourceBundle.</li>
96 * <li>Otherwise, retrieve the reply text from the <code>replyText</code> ResourceBundle,
97 * using the reply code as the key.</li>
98 * </ol>
99 * If the arguments Object[] is not null, then these arguments are substituted within the
100 * reply text using the {@link MessageFormat} class.
101 *
102 * @param session - the Session
103 * @param replyCode - the reply code
104 * @param replyMessageKey - if not null (and replyText is null), this is used as the ResourceBundle
105 * message key instead of the reply code.
106 * @param replyText - if non-null, this is used as the reply text
107 * @param arguments - the array of arguments to be formatted and substituted within the reply
108 * text; may be null
109 * @throws AssertFailedException - if session is null
110 * @see MessageFormat
111 */
112 protected void sendReply(Session session, int replyCode, String replyMessageKey, String replyText,
113 Object[] arguments) {
114
115 Assert.notNull(session, "session");
116 assertValidReplyCode(replyCode);
117
118 String key = (replyMessageKey != null) ? replyMessageKey : Integer.toString(replyCode);
119 String text = getTextForReplyCode(replyCode, key, replyText, arguments);
120 String replyTextToLog = (text == null) ? "" : " " + text;
121 LOG.info("Sending reply [" + replyCode + replyTextToLog + "]");
122 session.sendReply(replyCode, text);
123 }
124
125 // -------------------------------------------------------------------------
126 // InvocationHistory - Support for command history
127 // -------------------------------------------------------------------------
128
129 /**
130 * @return the number of invocation records stored for this command handler instance
131 * @see org.mockftpserver.core.command.InvocationHistory#numberOfInvocations()
132 */
133 public int numberOfInvocations() {
134 return invocations.size();
135 }
136
137 /**
138 * Return the InvocationRecord representing the command invoction data for the nth invocation
139 * for this command handler instance. One InvocationRecord should be stored for each invocation
140 * of the CommandHandler.
141 *
142 * @param index - the index of the invocation record to return. The first record is at index zero.
143 * @return the InvocationRecord for the specified index
144 * @throws AssertFailedException - if there is no invocation record corresponding to the specified index
145 * @see org.mockftpserver.core.command.InvocationHistory#getInvocation(int)
146 */
147 public InvocationRecord getInvocation(int index) {
148 return (InvocationRecord) invocations.get(index);
149 }
150
151 /**
152 * Clear out the invocation history for this CommandHandler. After invoking this method, the
153 * <code>numberOfInvocations()</code> method will return zero.
154 *
155 * @see org.mockftpserver.core.command.InvocationHistory#clearInvocations()
156 */
157 public void clearInvocations() {
158 invocations.clear();
159 }
160
161 // -------------------------------------------------------------------------
162 // Internal Helper Methods
163 // -------------------------------------------------------------------------
164
165 /**
166 * Return the text for the specified reply code, formatted using the message arguments, if
167 * supplied. If overrideText is not null, then return that. Otherwise, return the text mapped to
168 * the code from the replyText ResourceBundle. If the ResourceBundle contains no mapping, then
169 * return null.
170 * <p/>
171 * If arguments is not null, then the returned reply text if formatted using the
172 * {@link MessageFormat} class.
173 *
174 * @param code - the reply code
175 * @param messageKey - the key used to retrieve the reply text from the replyTextBundle
176 * @param overrideText - if not null, this is used instead of the text from the replyTextBundle.
177 * @param arguments - the array of arguments to be formatted and substituted within the reply
178 * text; may be null
179 * @return the text for the reply code; may be null
180 */
181 private String getTextForReplyCode(int code, String messageKey, String overrideText, Object[] arguments) {
182 try {
183 String t = (overrideText == null) ? getReplyTextBundle().getString(messageKey) : overrideText;
184 String formattedMessage = MessageFormat.format(t, arguments);
185 return (formattedMessage == null) ? null : formattedMessage.trim();
186 }
187 catch (MissingResourceException e) {
188 // No reply text is mapped for the specified key
189 LOG.warn("No reply text defined for reply code [" + code + "]");
190 return null;
191 }
192 }
193
194}