177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair/* 277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Copyright 2007 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.core.session; 1777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 1877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.apache.log4j.Logger; 1977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.MockFtpServerException; 2077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.Command; 2177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.CommandHandler; 2277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.command.CommandNames; 2377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.socket.DefaultServerSocketFactory; 2477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.socket.DefaultSocketFactory; 2577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.socket.ServerSocketFactory; 2677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.socket.SocketFactory; 2777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.util.Assert; 2877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport org.mockftpserver.core.util.AssertFailedException; 2977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 3077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.BufferedReader; 3177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.ByteArrayOutputStream; 3277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.IOException; 3377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.InputStream; 3477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.InputStreamReader; 3577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.OutputStream; 3677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.PrintWriter; 3777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.io.Writer; 3877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.net.InetAddress; 3977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.net.ServerSocket; 4077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.net.Socket; 4177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.net.SocketTimeoutException; 4277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.ArrayList; 4377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.HashMap; 4477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.List; 4577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.Map; 4677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.Set; 4777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairimport java.util.StringTokenizer; 4877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 4977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair/** 5077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Default implementation of the {@link Session} interface. 5177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 5277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @author Chris Mair 5377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @version $Revision$ - $Date$ 5477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 5577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismairpublic class DefaultSession implements Session { 5677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 5777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private static final Logger LOG = Logger.getLogger(DefaultSession.class); 5877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private static final String END_OF_LINE = "\r\n"; 5977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair protected static final int DEFAULT_CLIENT_DATA_PORT = 21; 6077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 6177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair protected SocketFactory socketFactory = new DefaultSocketFactory(); 6277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair protected ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); 6377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 6477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private BufferedReader controlConnectionReader; 6577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private Writer controlConnectionWriter; 6677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private Socket controlSocket; 6777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private Socket dataSocket; 6877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair ServerSocket passiveModeDataSocket; // non-private for testing 6977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private InputStream dataInputStream; 7077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private OutputStream dataOutputStream; 7177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private Map commandHandlers; 7277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private int clientDataPort = DEFAULT_CLIENT_DATA_PORT; 7377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private InetAddress clientHost; 7477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private InetAddress serverHost; 7577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private Map attributes = new HashMap(); 7677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private volatile boolean terminate = false; 7777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 7877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 7977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Create a new initialized instance 8077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 8177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param controlSocket - the control connection socket 8277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param commandHandlers - the Map of command name -> CommandHandler. It is assumed that the 8377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * command names are all normalized to upper case. See {@link Command#normalizeName(String)}. 8477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 8577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public DefaultSession(Socket controlSocket, Map commandHandlers) { 8677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(controlSocket, "controlSocket"); 8777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(commandHandlers, "commandHandlers"); 8877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 8977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.controlSocket = controlSocket; 9077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.commandHandlers = commandHandlers; 9177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.serverHost = controlSocket.getLocalAddress(); 9277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 9377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 9477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 9577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Return the InetAddress representing the client host for this session 9677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 9777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the client host 9877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#getClientHost() 9977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 10077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public InetAddress getClientHost() { 10177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return controlSocket.getInetAddress(); 10277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 10377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 10477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 10577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Return the InetAddress representing the server host for this session 10677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 10777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the server host 10877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#getServerHost() 10977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 11077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public InetAddress getServerHost() { 11177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return serverHost; 11277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 11377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 11477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 11577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Send the specified reply code and text across the control connection. 11677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * The reply text is trimmed before being sent. 11777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 11877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param code - the reply code 11977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param text - the reply text to send; may be null 12077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 12177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void sendReply(int code, String text) { 12277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair assertValidReplyCode(code); 12377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 12477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair StringBuffer buffer = new StringBuffer(Integer.toString(code)); 12577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 12677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (text != null && text.length() > 0) { 12777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair String replyText = text.trim(); 12877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (replyText.indexOf("\n") != -1) { 12977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair int lastIndex = replyText.lastIndexOf("\n"); 13077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append("-"); 13177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair for (int i = 0; i < replyText.length(); i++) { 13277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair char c = replyText.charAt(i); 13377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append(c); 13477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (i == lastIndex) { 13577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append(Integer.toString(code)); 13677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append(" "); 13777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 13877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 13977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } else { 14077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append(" "); 14177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair buffer.append(replyText); 14277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 14377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 14477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Sending Reply [" + buffer.toString() + "]"); 14577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair writeLineToControlConnection(buffer.toString()); 14677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 14777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 14877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 14977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#openDataConnection() 15077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 15177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void openDataConnection() { 15277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 15377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (passiveModeDataSocket != null) { 15477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Waiting for (passive mode) client connection from client host [" + clientHost 15577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair + "] on port " + passiveModeDataSocket.getLocalPort()); 15677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair // TODO set socket timeout 15777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 15877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataSocket = passiveModeDataSocket.accept(); 15977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Successful (passive mode) client connection to port " 16077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair + passiveModeDataSocket.getLocalPort()); 16177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 16277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (SocketTimeoutException e) { 16377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 16477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 16577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } else { 16677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(clientHost, "clientHost"); 16777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Connecting to client host [" + clientHost + "] on data port [" + clientDataPort 16877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair + "]"); 16977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataSocket = socketFactory.createSocket(clientHost, clientDataPort); 17077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 17177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataOutputStream = dataSocket.getOutputStream(); 17277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataInputStream = dataSocket.getInputStream(); 17377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 17477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 17577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 17677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 17777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 17877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 17977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 18077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Switch to passive mode 18177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 18277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the local port to be connected to by clients for data transfers 18377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#switchToPassiveMode() 18477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 18577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public int switchToPassiveMode() { 18677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 18777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair passiveModeDataSocket = serverSocketFactory.createServerSocket(0); 18877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return passiveModeDataSocket.getLocalPort(); 18977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 19077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 19177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException("Error opening passive mode server data socket", e); 19277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 19377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 19477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 19577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 19677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#closeDataConnection() 19777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 19877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void closeDataConnection() { 19977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 20077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Flushing and closing client data socket"); 20177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataOutputStream.flush(); 20277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataOutputStream.close(); 20377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataInputStream.close(); 20477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataSocket.close(); 20577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 20677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 20777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.error("Error closing client data socket", e); 20877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 20977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 21077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 21177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 21277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Write a single line to the control connection, appending a newline 21377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 21477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param line - the line to write 21577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 21677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private void writeLineToControlConnection(String line) { 21777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 21877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionWriter.write(line + END_OF_LINE); 21977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionWriter.flush(); 22077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 22177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 22277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.error("Error writing to control connection", e); 22377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException("Error writing to control connection", e); 22477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 22577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 22677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 22777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 22877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#close() 22977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 23077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void close() { 23177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.trace("close()"); 23277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair terminate = true; 23377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 23477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 23577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 23677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#sendData(byte[], int) 23777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 23877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void sendData(byte[] data, int numBytes) { 23977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(data, "data"); 24077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 24177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair dataOutputStream.write(data, 0, numBytes); 24277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 24377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 24477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 24577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 24677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 24777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 24877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 24977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#readData() 25077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 25177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public byte[] readData() { 25277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 25377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 25477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 25577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 25677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair while (true) { 25777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair int b = dataInputStream.read(); 25877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (b == -1) { 25977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair break; 26077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 26177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair bytes.write(b); 26277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 26377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return bytes.toByteArray(); 26477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 26577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 26677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 26777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 26877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 26977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 27077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 27177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Wait for and read the command sent from the client on the control connection. 27277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 27377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the Command sent from the client; may be null if the session has been closed 27477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * <p/> 27577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Package-private to enable testing 27677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 27777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Command readCommand() { 27877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 27977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair final long socketReadIntervalMilliseconds = 100L; 28077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 28177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 28277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair while (true) { 28377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (terminate) { 28477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return null; 28577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 28677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair // Don't block; only read command when it is available 28777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (controlConnectionReader.ready()) { 28877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair String command = controlConnectionReader.readLine(); 28977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.info("Received command: [" + command + "]"); 29077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return parseCommand(command); 29177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 29277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 29377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Thread.sleep(socketReadIntervalMilliseconds); 29477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 29577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (InterruptedException e) { 29677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 29777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 29877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 29977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 30077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 30177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.error("Read failed", e); 30277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 30377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 30477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 30577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 30677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 30777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Parse the command String into a Command object 30877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 30977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param commandString - the command String 31077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the Command object parsed from the command String 31177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 31277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Command parseCommand(String commandString) { 31377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNullOrEmpty(commandString, "commandString"); 31477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 31577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair List parameters = new ArrayList(); 31677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair String name; 31777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 31877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair int indexOfFirstSpace = commandString.indexOf(" "); 31977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (indexOfFirstSpace != -1) { 32077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair name = commandString.substring(0, indexOfFirstSpace); 32177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair StringTokenizer tokenizer = new StringTokenizer(commandString.substring(indexOfFirstSpace + 1), 32277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair ","); 32377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair while (tokenizer.hasMoreTokens()) { 32477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair parameters.add(tokenizer.nextToken()); 32577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 32677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } else { 32777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair name = commandString; 32877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 32977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 33077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair String[] parametersArray = new String[parameters.size()]; 33177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return new Command(name, (String[]) parameters.toArray(parametersArray)); 33277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 33377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 33477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 33577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#setClientDataHost(java.net.InetAddress) 33677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 33777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void setClientDataHost(InetAddress clientHost) { 33877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.clientHost = clientHost; 33977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 34077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 34177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 34277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#setClientDataPort(int) 34377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 34477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void setClientDataPort(int dataPort) { 34577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.clientDataPort = dataPort; 34677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 34777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair // Clear out any passive data connection mode information 34877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (passiveModeDataSocket != null) { 34977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 35077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair this.passiveModeDataSocket.close(); 35177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 35277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 35377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 35477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 35577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair passiveModeDataSocket = null; 35677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 35777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 35877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 35977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 36077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see java.lang.Runnable#run() 36177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 36277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void run() { 36377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 36477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 36577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair InputStream inputStream = controlSocket.getInputStream(); 36677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair OutputStream outputStream = controlSocket.getOutputStream(); 36777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionReader = new BufferedReader(new InputStreamReader(inputStream)); 36877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionWriter = new PrintWriter(outputStream, true); 36977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 37077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Starting the session..."); 37177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 37277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair CommandHandler connectCommandHandler = (CommandHandler) commandHandlers.get(CommandNames.CONNECT); 37377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair connectCommandHandler.handleCommand(new Command(CommandNames.CONNECT, new String[0]), this); 37477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 37577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair while (!terminate) { 37677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair readAndProcessCommand(); 37777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 37877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 37977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (Exception e) { 38077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.error(e); 38177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair throw new MockFtpServerException(e); 38277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 38377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair finally { 38477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Cleaning up the session"); 38577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair try { 38677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionReader.close(); 38777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair controlConnectionWriter.close(); 38877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 38977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair catch (IOException e) { 39077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.error(e); 39177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 39277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair LOG.debug("Session stopped."); 39377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 39477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 39577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 39677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 39777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Read and process the next command from the control connection 39877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 39977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @throws Exception - if any error occurs 40077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 40177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private void readAndProcessCommand() throws Exception { 40277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 40377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Command command = readCommand(); 40477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (command != null) { 40577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair String normalizedCommandName = Command.normalizeName(command.getName()); 40677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair CommandHandler commandHandler = (CommandHandler) commandHandlers.get(normalizedCommandName); 40777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 40877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair if (commandHandler == null) { 40977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair commandHandler = (CommandHandler) commandHandlers.get(CommandNames.UNSUPPORTED); 41077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 41177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 41277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(commandHandler, "CommandHandler for command [" + normalizedCommandName + "]"); 41377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair commandHandler.handleCommand(command, this); 41477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 41577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 41677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 41777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 41877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Assert that the specified number is a valid reply code 41977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 42077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param replyCode - the reply code to check 42177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 42277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair private void assertValidReplyCode(int replyCode) { 42377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.isTrue(replyCode > 0, "The number [" + replyCode + "] is not a valid reply code"); 42477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 42577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 42677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 42777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Return the attribute value for the specified name. Return null if no attribute value 42877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * exists for that name or if the attribute value is null. 42977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 43077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param name - the attribute name; may not be null 43177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the value of the attribute stored under name; may be null 43277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#getAttribute(java.lang.String) 43377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 43477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public Object getAttribute(String name) { 43577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(name, "name"); 43677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return attributes.get(name); 43777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 43877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 43977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 44077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Store the value under the specified attribute name. 44177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 44277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param name - the attribute name; may not be null 44377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param value - the attribute value; may be null 44477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#setAttribute(java.lang.String, java.lang.Object) 44577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 44677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void setAttribute(String name, Object value) { 44777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(name, "name"); 44877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair attributes.put(name, value); 44977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 45077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 45177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 45277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Return the Set of names under which attributes have been stored on this session. 45377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Returns an empty Set if no attribute values are stored. 45477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 45577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @return the Set of attribute names 45677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#getAttributeNames() 45777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 45877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public Set getAttributeNames() { 45977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair return attributes.keySet(); 46077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 46177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 46277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair /** 46377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * Remove the attribute value for the specified name. Do nothing if no attribute 46477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * value is stored for the specified name. 46577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * 46677b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @param name - the attribute name; may not be null 46777b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @throws AssertFailedException - if name is null 46877b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair * @see org.mockftpserver.core.session.Session#removeAttribute(java.lang.String) 46977b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair */ 47077b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair public void removeAttribute(String name) { 47177b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair Assert.notNull(name, "name"); 47277b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair attributes.remove(name); 47377b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair } 47477b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair 47577b8661f08d1379c0bdf2af93d8004fced9f1ab0chrismair} 476