177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair/*
277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Copyright 2008 the original author or authors.
377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair *
477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Licensed under the Apache License, Version 2.0 (the "License");
577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * you may not use this file except in compliance with the License.
677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * You may obtain a copy of the License at
777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair *
877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair *      http://www.apache.org/licenses/LICENSE-2.0
977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair *
1077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Unless required by applicable law or agreed to in writing, software
1177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * distributed under the License is distributed on an "AS IS" BASIS,
1277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * See the License for the specific language governing permissions and
1477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * limitations under the License.
1577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */
1677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairpackage org.mockftpserver.fake.command;
1777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
1877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.CommandSyntaxException;
1977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.IllegalStateException;
2077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.NotLoggedInException;
2177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.AbstractCommandHandler;
2277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.Command;
2377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.ReplyCodes;
2477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.session.Session;
2577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.session.SessionKeys;
2677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.util.Assert;
2777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.ServerConfiguration;
2877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.ServerConfigurationAware;
2977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.UserAccount;
3077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.filesystem.FileSystem;
3177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.filesystem.FileSystemEntry;
3277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.filesystem.FileSystemException;
3377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.fake.filesystem.InvalidFilenameException;
3477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
3577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.text.MessageFormat;
3677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.ArrayList;
3777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.Collections;
3877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.List;
3977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.MissingResourceException;
4077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
4177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair/**
4277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Abstract superclass for CommandHandler classes for the "Fake" server.
4377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair *
4477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @author Chris Mair
4577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @version $Revision$ - $Date$
4677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */
4777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairpublic abstract class AbstractFakeCommandHandler extends AbstractCommandHandler implements ServerConfigurationAware {
4877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
4977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected static final String INTERNAL_ERROR_KEY = "internalError";
5077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
5177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    private ServerConfiguration serverConfiguration;
5277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
5377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
5477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Reply code sent back when a FileSystemException is caught by the                 {@link #handleCommand(Command, Session)}
5577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * This defaults to ReplyCodes.EXISTING_FILE_ERROR (550).
5677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
5777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected int replyCodeForFileSystemException = ReplyCodes.READ_FILE_ERROR;
5877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
5977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    public ServerConfiguration getServerConfiguration() {
6077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return serverConfiguration;
6177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
6277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
6377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    public void setServerConfiguration(ServerConfiguration serverConfiguration) {
6477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        this.serverConfiguration = serverConfiguration;
6577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
6677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
6777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
6877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Use template method to centralize and ensure common validation
6977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
7077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    public void handleCommand(Command command, Session session) {
7177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        Assert.notNull(serverConfiguration, "serverConfiguration");
7277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        Assert.notNull(command, "command");
7377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        Assert.notNull(session, "session");
7477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
7577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        try {
7677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handle(command, session);
7777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
7877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (CommandSyntaxException e) {
7977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handleException(command, session, e, ReplyCodes.COMMAND_SYNTAX_ERROR);
8077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
8177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (IllegalStateException e) {
8277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handleException(command, session, e, ReplyCodes.ILLEGAL_STATE);
8377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
8477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (NotLoggedInException e) {
8577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handleException(command, session, e, ReplyCodes.NOT_LOGGED_IN);
8677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
8777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (InvalidFilenameException e) {
8877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handleFileSystemException(command, session, e, ReplyCodes.FILENAME_NOT_VALID, e.getPath());
8977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
9077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (FileSystemException e) {
9177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            handleFileSystemException(command, session, e, replyCodeForFileSystemException, e.getPath());
9277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
9377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
9477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
9577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
9677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Convenience method to return the FileSystem stored in the ServerConfiguration
9777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
9877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return the FileSystem
9977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
10077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected FileSystem getFileSystem() {
10177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return serverConfiguration.getFileSystem();
10277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
10377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
10477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
10577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Handle the specified command for the session. All checked exceptions are expected to be wrapped or handled
10677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * by the caller.
10777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
10877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param command - the Command to be handled
10977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the session on which the Command was submitted
11077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
11177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected abstract void handle(Command command, Session session);
11277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
11377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // -------------------------------------------------------------------------
11477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // Utility methods for subclasses
11577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // -------------------------------------------------------------------------
11677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
11777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
11877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Send a reply for this command on the control connection.
11977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * <p/>
12077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * The reply code is designated by the <code>replyCode</code> property, and the reply text
12177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is retrieved from the <code>replyText</code> ResourceBundle, using the specified messageKey.
12277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
12377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session    - the Session
12477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode  - the reply code
12577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param messageKey - the resource bundle key for the reply text
12677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws AssertionError - if session is null
12777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @see MessageFormat
12877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
12977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void sendReply(Session session, int replyCode, String messageKey) {
13077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode, messageKey, Collections.EMPTY_LIST);
13177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
13277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
13377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
13477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Send a reply for this command on the control connection.
13577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * <p/>
13677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * The reply code is designated by the <code>replyCode</code> property, and the reply text
13777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is retrieved from the <code>replyText</code> ResourceBundle, using the specified messageKey.
13877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
13977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session    - the Session
14077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode  - the reply code
14177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param messageKey - the resource bundle key for the reply text
14277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param args       - the optional message arguments; defaults to []
14377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws AssertionError - if session is null
14477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @see MessageFormat
14577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
14677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void sendReply(Session session, int replyCode, String messageKey, List args) {
14777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        Assert.notNull(session, "session");
14877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        assertValidReplyCode(replyCode);
14977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
15077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String text = getTextForKey(messageKey);
15177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String replyText = (args != null && !args.isEmpty()) ? MessageFormat.format(text, args.toArray()) : text;
15277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
15377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String replyTextToLog = (replyText == null) ? "" : " " + replyText;
15477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String argsToLog = (args != null && !args.isEmpty()) ? (" args=" + args) : "";
15577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        LOG.info("Sending reply [" + replyCode + replyTextToLog + "]" + argsToLog);
15677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        session.sendReply(replyCode, replyText);
15777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
15877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
15977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
16077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Send a reply for this command on the control connection.
16177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * <p/>
16277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * The reply code is designated by the <code>replyCode</code> property, and the reply text
16377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is retrieved from the <code>replyText</code> ResourceBundle, using the reply code as the key.
16477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
16577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session   - the Session
16677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode - the reply code
16777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws AssertionError - if session is null
16877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @see MessageFormat
16977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
17077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void sendReply(Session session, int replyCode) {
17177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode, Collections.EMPTY_LIST);
17277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
17377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
17477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
17577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Send a reply for this command on the control connection.
17677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * <p/>
17777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * The reply code is designated by the <code>replyCode</code> property, and the reply text
17877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is retrieved from the <code>replyText</code> ResourceBundle, using the reply code as the key.
17977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
18077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session   - the Session
18177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode - the reply code
18277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param args      - the optional message arguments; defaults to []
18377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws AssertionError - if session is null
18477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @see MessageFormat
18577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
18677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void sendReply(Session session, int replyCode, List args) {
18777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode, Integer.toString(replyCode), args);
18877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
18977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
19077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
19177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Handle the exception caught during handleCommand()
19277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
19377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param command   - the Command
19477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session   - the Session
19577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param exception - the caught exception
19677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode - the reply code that should be sent back
19777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
19877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    private void handleException(Command command, Session session, Throwable exception, int replyCode) {
19977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        LOG.warn("Error handling command: " + command + "; " + exception, exception);
20077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode);
20177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
20277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
20377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
20477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Handle the exception caught during handleCommand()
20577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
20677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param command   - the Command
20777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session   - the Session
20877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param exception - the caught exception
20977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode - the reply code that should be sent back
21077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param arg       - the arg for the reply (message)
21177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
21277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    private void handleFileSystemException(Command command, Session session, FileSystemException exception, int replyCode, Object arg) {
21377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        LOG.warn("Error handling command: " + command + "; " + exception, exception);
21477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode, exception.getMessageKey(), Collections.singletonList(arg));
21577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
21677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
21777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
21877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Return the value of the named attribute within the session.
21977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
22077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
22177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param name    - the name of the session attribute to retrieve
22277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return the value of the named session attribute
22377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws IllegalStateException - if the Session does not contain the named attribute
22477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
22577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected Object getRequiredSessionAttribute(Session session, String name) {
22677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        Object value = session.getAttribute(name);
22777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (value == null) {
22877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            throw new IllegalStateException("Session missing required attribute [" + name + "]");
22977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
23077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return value;
23177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
23277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
23377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
23477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Verify that the current user (if any) has already logged in successfully.
23577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
23677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
23777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
23877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void verifyLoggedIn(Session session) {
23977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (getUserAccount(session) == null) {
24077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            throw new NotLoggedInException("User has not logged in");
24177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
24277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
24377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
24477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
24577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
24677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return the UserAccount stored in the specified session; may be null
24777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
24877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected UserAccount getUserAccount(Session session) {
24977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return (UserAccount) session.getAttribute(SessionKeys.USER_ACCOUNT);
25077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
25177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
25277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
25377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Verify that the specified condition related to the file system is true,
25477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * otherwise throw a FileSystemException.
25577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
25677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param condition  - the condition that must be true
25777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param path       - the path involved in the operation; this will be included in the
25877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *                   error message if the condition is not true.
25977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param messageKey - the message key for the exception message
26077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws FileSystemException - if the condition is not true
26177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
26277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void verifyFileSystemCondition(boolean condition, String path, String messageKey) {
26377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (!condition) {
26477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            throw new FileSystemException(path, messageKey);
26577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
26677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
26777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
26877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
26977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Verify that the current user has execute permission to the specified path
27077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
27177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
27277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param path    - the file system path
27377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws FileSystemException - if the condition is not true
27477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
27577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void verifyExecutePermission(Session session, String path) {
27677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        UserAccount userAccount = getUserAccount(session);
27777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        FileSystemEntry entry = getFileSystem().getEntry(path);
27877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        verifyFileSystemCondition(userAccount.canExecute(entry), path, "filesystem.cannotExecute");
27977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
28077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
28177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
28277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Verify that the current user has write permission to the specified path
28377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
28477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
28577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param path    - the file system path
28677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws FileSystemException - if the condition is not true
28777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
28877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void verifyWritePermission(Session session, String path) {
28977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        UserAccount userAccount = getUserAccount(session);
29077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        FileSystemEntry entry = getFileSystem().getEntry(path);
29177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        verifyFileSystemCondition(userAccount.canWrite(entry), path, "filesystem.cannotWrite");
29277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
29377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
29477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
29577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Verify that the current user has read permission to the specified path
29677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
29777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
29877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param path    - the file system path
29977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @throws FileSystemException - if the condition is not true
30077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
30177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void verifyReadPermission(Session session, String path) {
30277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        UserAccount userAccount = getUserAccount(session);
30377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        FileSystemEntry entry = getFileSystem().getEntry(path);
30477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        verifyFileSystemCondition(userAccount.canRead(entry), path, "filesystem.cannotRead");
30577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
30677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
30777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
30877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Return the full, absolute path for the specified abstract pathname.
30977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * If path is null, return the current directory (stored in the session). If
31077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * path represents an absolute path, then return path as is. Otherwise, path
31177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is relative, so assemble the full path from the current directory
31277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * and the specified relative path.
31377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
31477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session - the Session
31577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param path    - the abstract pathname; may be null
31677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return the resulting full, absolute path
31777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
31877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected String getRealPath(Session session, String path) {
31977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String currentDirectory = (String) session.getAttribute(SessionKeys.CURRENT_DIRECTORY);
32077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (path == null) {
32177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return currentDirectory;
32277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
32377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (getFileSystem().isAbsolute(path)) {
32477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return path;
32577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
32677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return getFileSystem().path(currentDirectory, path);
32777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
32877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
32977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
33077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Return the end-of-line character(s) used when building multi-line responses
33177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
33277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return "\r\n"
33377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
33477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected String endOfLine() {
33577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return "\r\n";
33677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
33777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
33877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    private String getTextForKey(String key) {
33977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String msgKey = (key != null) ? key : INTERNAL_ERROR_KEY;
34077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        try {
34177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return getReplyTextBundle().getString(msgKey);
34277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
34377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        catch (MissingResourceException e) {
34477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            // No reply text is mapped for the specified key
34577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            LOG.warn("No reply text defined for key [" + msgKey + "]");
34677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return null;
34777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
34877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
34977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
35077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // -------------------------------------------------------------------------
35177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // Login Support (used by USER and PASS commands)
35277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    // -------------------------------------------------------------------------
35377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
35477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
35577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Validate the UserAccount for the specified username. If valid, return true. If the UserAccount does
35677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * not exist or is invalid, log an error message, send back a reply code of 530 with an appropriate
35777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * error message, and return false. A UserAccount is considered invalid if the homeDirectory property
35877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * is not set or is set to a non-existent directory.
35977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
36077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param username - the username
36177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session  - the session; used to send back an error reply if necessary
36277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return true only if the UserAccount for the named user is valid
36377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
36477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected boolean validateUserAccount(String username, Session session) {
36577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        UserAccount userAccount = serverConfiguration.getUserAccount(username);
36677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (userAccount == null || !userAccount.isValid()) {
36777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            LOG.error("UserAccount missing or not valid for username [" + username + "]: " + userAccount);
36877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            sendReply(session, ReplyCodes.USER_ACCOUNT_NOT_VALID, "login.userAccountNotValid", list(username));
36977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return false;
37077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
37177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
37277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        String home = userAccount.getHomeDirectory();
37377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        if (!getFileSystem().isDirectory(home)) {
37477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            LOG.error("Home directory configured for username [" + username + "] is not valid: " + home);
37577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            sendReply(session, ReplyCodes.USER_ACCOUNT_NOT_VALID, "login.homeDirectoryNotValid", list(username, home));
37677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair            return false;
37777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        }
37877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
37977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return true;
38077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
38177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
38277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
38377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Log in the specified user for the current session. Send back a reply of 230 with a message indicated
38477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * by the replyMessageKey and set the UserAccount and current directory (homeDirectory) in the session.
38577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
38677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param userAccount     - the userAccount for the user to be logged in
38777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param session         - the session
38877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyCode       - the reply code to send
38977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param replyMessageKey - the message key for the reply text
39077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
39177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected void login(UserAccount userAccount, Session session, int replyCode, String replyMessageKey) {
39277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        sendReply(session, replyCode, replyMessageKey);
39377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        session.setAttribute(SessionKeys.USER_ACCOUNT, userAccount);
39477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        session.setAttribute(SessionKeys.CURRENT_DIRECTORY, userAccount.getHomeDirectory());
39577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
39677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
39777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
39877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Convenience method to return a List with the specified single item
39977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
40077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param item - the single item in the returned List
40177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return a new List with that single item
40277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
40377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected List list(Object item) {
40477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return Collections.singletonList(item);
40577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
40677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
40777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
40877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Convenience method to return a List with the specified two items
40977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
41077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param item1 - the first item in the returned List
41177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param item2 - the second item in the returned List
41277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return a new List with the specified items
41377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
41477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected List list(Object item1, Object item2) {
41577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        List list = new ArrayList(2);
41677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        list.add(item1);
41777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        list.add(item2);
41877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return list;
41977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
42077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
42177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
42277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Return true if the specified string is null or empty
42377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
42477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param string - the String to check; may be null
42577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return true only if the specified String is null or empyt
42677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
42777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected boolean notNullOrEmpty(String string) {
42877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return string != null && string.length() > 0;
42977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
43077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
43177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    /**
43277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * Return the string unless it is null or empty, in which case return the defaultString.
43377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     *
43477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param string        - the String to check; may be null
43577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @param defaultString - the value to return if string is null or empty
43677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     * @return string if not null and not empty; otherwise return defaultString
43777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair     */
43877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    protected String defaultIfNullOrEmpty(String string, String defaultString) {
43977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair        return (notNullOrEmpty(string) ? string : defaultString);
44077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair    }
44177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair
44277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair}