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.core.command;
     17 
     18 import org.mockftpserver.core.CommandSyntaxException;
     19 import org.mockftpserver.core.session.Session;
     20 import org.mockftpserver.core.util.Assert;
     21 import org.mockftpserver.core.util.AssertFailedException;
     22 
     23 import java.text.MessageFormat;
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 import 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  */
     35 public 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 }
    195