19d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair/* 29d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Copyright 2007 the original author or authors. 39d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 49d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Licensed under the Apache License, Version 2.0 (the "License"); 59d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * you may not use this file except in compliance with the License. 69d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * You may obtain a copy of the License at 79d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 89d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * http://www.apache.org/licenses/LICENSE-2.0 99d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 109d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Unless required by applicable law or agreed to in writing, software 119d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * distributed under the License is distributed on an "AS IS" BASIS, 129d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * See the License for the specific language governing permissions and 149d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * limitations under the License. 159d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 169d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairpackage org.mockftpserver.core.session; 179d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 189d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.BufferedReader; 199d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.ByteArrayOutputStream; 209d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.IOException; 219d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.InputStream; 229d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.InputStreamReader; 239d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.OutputStream; 249d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.PrintWriter; 259d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.io.Writer; 269d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.net.InetAddress; 279d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.net.ServerSocket; 289d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.net.Socket; 299d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.net.SocketTimeoutException; 309d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.ArrayList; 319d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.HashMap; 329d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.List; 339d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.Map; 349d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.Set; 359d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport java.util.StringTokenizer; 369d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 379d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.apache.log4j.Logger; 389d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.MockFtpServerException; 399d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.command.Command; 409d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.command.CommandHandler; 419d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.command.CommandNames; 429d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.socket.DefaultServerSocketFactory; 439d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.socket.DefaultSocketFactory; 449d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.socket.ServerSocketFactory; 459d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.socket.SocketFactory; 469d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.util.Assert; 479d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairimport org.mockftpserver.core.util.AssertFailedException; 489d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 499d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair/** 509d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Default implementation of the {@link Session} interface. 519d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 529d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @version $Revision$ - $Date$ 539d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 549d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @author Chris Mair 559d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 569d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismairpublic class DefaultSession implements Session { 579d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 589d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private static final Logger LOG = Logger.getLogger(DefaultSession.class); 599d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair static final int DEFAULT_CLIENT_DATA_PORT = 21; 609d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 619d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair SocketFactory socketFactory = new DefaultSocketFactory(); 629d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); 639d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 649d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private BufferedReader controlConnectionReader; 659d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private Writer controlConnectionWriter; 669d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private Socket controlSocket; 679d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private Socket dataSocket; 689d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair ServerSocket passiveModeDataSocket; // non-private for testing 699d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private InputStream dataInputStream; 709d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private OutputStream dataOutputStream; 719d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private Map commandHandlers; 729d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private int clientDataPort = DEFAULT_CLIENT_DATA_PORT; 739d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private InetAddress clientHost; 749d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private InetAddress serverHost; 759d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private Map attributes = new HashMap(); 769d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private volatile boolean terminate = false; 779d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 789d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 799d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Create a new initialized instance 809d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 819d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param controlSocket - the control connection socket 829d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param commandHandlers - the Map of command name -> CommandHandler. It is assumed that the 839d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * command names are all normalized to upper case. See {@link Command#normalizeName(String)}. 849d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 859d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public DefaultSession(Socket controlSocket, Map commandHandlers) { 869d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(controlSocket, "controlSocket"); 879d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(commandHandlers, "commandHandlers"); 889d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 899d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.controlSocket = controlSocket; 909d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.commandHandlers = commandHandlers; 919d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.serverHost = controlSocket.getLocalAddress(); 929d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 939d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 949d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 959d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Return the InetAddress representing the client host for this session 969d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 979d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the client host 989d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 999d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#getClientHost() 1009d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 1019d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public InetAddress getClientHost() { 1029d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return controlSocket.getInetAddress(); 1039d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1049d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1059d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 1069d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Return the InetAddress representing the server host for this session 1079d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 1089d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the server host 1099d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 1109d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#getServerHost() 1119d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 1129d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public InetAddress getServerHost() { 1139d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return serverHost; 1149d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1159d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1169d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 1179d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Send the specified reply code and text across the control connection. 1189d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * The reply text is trimmed before being sent. 1199d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 1209d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param code - the reply code 1219d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param text - the reply text to send; may be null 1229d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 1239d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void sendReply(int code, String text) { 1249d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair assertValidReplyCode(code); 1259d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1269d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair StringBuffer buffer = new StringBuffer(Integer.toString(code)); 1279d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1289d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (text != null && text.length() > 0) { 1299d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair String replyText = text.trim(); 1309d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (replyText.indexOf("\n") != -1) { 1319d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair int lastIndex = replyText.lastIndexOf("\n"); 1329d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair buffer.append("-"); 1339d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair for (int i = 0; i < replyText.length(); i++) { 1349d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair char c = replyText.charAt(i); 1359d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair buffer.append(c); 1369d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (i == lastIndex) { 1379d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair buffer.append(Integer.toString(code) + " "); 1389d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1399d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1409d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1419d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair else { 1429d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair buffer.append(" "); 1439d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair buffer.append(replyText); 1449d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1459d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1469d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Sending Reply [" + buffer.toString() + "]"); 1479d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair writeLineToControlConnection(buffer.toString()); 1489d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1499d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1509d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 1519d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#openDataConnection() 1529d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 1539d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void openDataConnection() { 1549d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 1559d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (passiveModeDataSocket != null) { 1569d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Waiting for (passive mode) client connection from client host [" + clientHost 1579d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair + "] on port " + passiveModeDataSocket.getLocalPort()); 1589d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair // TODO set socket timeout 1599d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 1609d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataSocket = passiveModeDataSocket.accept(); 1619d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Successful (passive mode) client connection to port " 1629d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair + passiveModeDataSocket.getLocalPort()); 1639d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1649d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (SocketTimeoutException e) { 1659d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 1669d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1679d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1689d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair else { 1699d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(clientHost, "clientHost"); 1709d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Connecting to client host [" + clientHost + "] on data port [" + clientDataPort 1719d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair + "]"); 1729d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataSocket = socketFactory.createSocket(clientHost, clientDataPort); 1739d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1749d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataOutputStream = dataSocket.getOutputStream(); 1759d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataInputStream = dataSocket.getInputStream(); 1769d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1779d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 1789d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 1799d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1809d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1819d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1829d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 1839d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Switch to passive mode 1849d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 1859d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the local port to be connected to by clients for data transfers 1869d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 1879d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#switchToPassiveMode() 1889d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 1899d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public int switchToPassiveMode() { 1909d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 1919d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair passiveModeDataSocket = serverSocketFactory.createServerSocket(0); 1929d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return passiveModeDataSocket.getLocalPort(); 1939d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1949d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 1959d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException("Error opening passive mode server data socket", e); 1969d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1979d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 1989d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 1999d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2009d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#closeDataConnection() 2019d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2029d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void closeDataConnection() { 2039d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2049d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Flushing and closing client data socket"); 2059d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataOutputStream.flush(); 2069d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataOutputStream.close(); 2079d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataInputStream.close(); 2089d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataSocket.close(); 2099d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2109d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 2119d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.error("Error closing client data socket", e); 2129d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2139d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2149d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2159d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2169d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Write a single line to the control connection, appending a newline 2179d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 2189d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param line - the line to write 2199d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2209d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private void writeLineToControlConnection(String line) { 2219d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2229d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionWriter.write(line + "\n"); 2239d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionWriter.flush(); 2249d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2259d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 2269d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.error("Error writing to control connection", e); 2279d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException("Error writing to control connection", e); 2289d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2299d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2309d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2319d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2329d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#close() 2339d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2349d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void close() { 2359d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.trace("close()"); 2369d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair terminate = true; 2379d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2389d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2399d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2409d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#sendData(byte[], int) 2419d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2429d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void sendData(byte[] data, int numBytes) { 2439d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(data, "data"); 2449d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2459d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair dataOutputStream.write(data, 0, numBytes); 2469d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2479d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 2489d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 2499d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2509d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2519d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2529d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2539d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#readData() 2549d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2559d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public byte[] readData() { 2569d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2579d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 2589d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2599d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2609d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair while (true) { 2619d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair int b = dataInputStream.read(); 2629d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (b == -1) { 2639d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair break; 2649d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2659d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair bytes.write(b); 2669d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2679d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return bytes.toByteArray(); 2689d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2699d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 2709d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 2719d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2729d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2739d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2749d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 2759d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Wait for and read the command sent from the client on the control connection. 2769d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 2779d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the Command sent from the client; may be null if the session has been closed 2789d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 2799d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Package-private to enable testing 2809d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 2819d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Command readCommand() { 2829d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2839d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair final long socketReadIntervalMilliseconds = 100L; 2849d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 2859d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2869d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair while (true) { 2879d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (terminate) { 2889d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return null; 2899d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2909d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair // Don't block; only read command when it is available 2919d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (controlConnectionReader.ready()) { 2929d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair String command = controlConnectionReader.readLine(); 2939d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.info("Received command: [" + command + "]"); 2949d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return parseCommand(command); 2959d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2969d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 2979d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Thread.sleep(socketReadIntervalMilliseconds); 2989d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 2999d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (InterruptedException e) { 3009d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 3019d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3029d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3039d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3049d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 3059d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.error("Read failed", e); 3069d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 3079d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3089d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3099d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3109d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 3119d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Parse the command String into a Command object 3129d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 3139d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param commandString - the command String 3149d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the Command object parsed from the command String 3159d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 3169d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Command parseCommand(String commandString) { 3179d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNullOrEmpty(commandString, "commandString"); 3189d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3199d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair List parameters = new ArrayList(); 3209d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair String name; 3219d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3229d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair int indexOfFirstSpace = commandString.indexOf(" "); 3239d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (indexOfFirstSpace != -1) { 3249d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair name = commandString.substring(0, indexOfFirstSpace); 3259d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair StringTokenizer tokenizer = new StringTokenizer(commandString.substring(indexOfFirstSpace + 1), 3269d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair ","); 3279d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair while (tokenizer.hasMoreTokens()) { 3289d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair parameters.add(tokenizer.nextToken()); 3299d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3309d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3319d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair else { 3329d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair name = commandString; 3339d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3349d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3359d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair String[] parametersArray = new String[parameters.size()]; 3369d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return new Command(name, (String[]) parameters.toArray(parametersArray)); 3379d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3389d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3399d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 3409d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#setClientDataHost(java.net.InetAddress) 3419d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 3429d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void setClientDataHost(InetAddress clientHost) { 3439d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.clientHost = clientHost; 3449d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3459d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3469d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 3479d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#setClientDataPort(int) 3489d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 3499d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void setClientDataPort(int dataPort) { 3509d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.clientDataPort = dataPort; 3519d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3529d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair // Clear out any passive data connection mode information 3539d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (passiveModeDataSocket != null) { 3549d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 3559d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair this.passiveModeDataSocket.close(); 3569d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3579d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 3589d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 3599d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3609d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair passiveModeDataSocket = null; 3619d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3629d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3639d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3649d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 3659d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see java.lang.Runnable#run() 3669d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 3679d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void run() { 3689d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 3699d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3709d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair InputStream inputStream = controlSocket.getInputStream(); 3719d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair OutputStream outputStream = controlSocket.getOutputStream(); 3729d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionReader = new BufferedReader(new InputStreamReader(inputStream)); 3739d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionWriter = new PrintWriter(outputStream, true); 3749d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3759d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Starting the session..."); 3769d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3779d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair CommandHandler connectCommandHandler = (CommandHandler) commandHandlers.get(CommandNames.CONNECT); 3789d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair connectCommandHandler.handleCommand(new Command(CommandNames.CONNECT, new String[0]), this); 3799d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 3809d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair while (!terminate) { 3819d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair readAndProcessCommand(); 3829d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3839d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3849d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (Exception e) { 3859d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.error(e); 3869d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 3879d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3889d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair finally { 3899d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Cleaning up the session"); 3909d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair try { 3919d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionReader.close(); 3929d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair controlConnectionWriter.close(); 3939d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3949d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair catch (IOException e) { 3959d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.error(e); 3969d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair throw new MockFtpServerException(e); 3979d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 3989d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair LOG.debug("Session stopped."); 3999d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4009d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4019d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4029d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4039d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Read and process the next command from the control connection 4049d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4059d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @throws Exception 4069d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4079d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private void readAndProcessCommand() throws Exception { 4089d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4099d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Command command = readCommand(); 4109d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair if (command != null) { 4119d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair String normalizedCommandName = Command.normalizeName(command.getName()); 4129d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair CommandHandler commandHandler = (CommandHandler) commandHandlers.get(normalizedCommandName); 4139d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(commandHandler, "CommandHandler for command [" + normalizedCommandName + "]"); 4149d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair commandHandler.handleCommand(command, this); 4159d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4169d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4179d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4189d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4199d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Assert that the specified number is a valid reply code 4209d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4219d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param replyCode - the reply code to check 4229d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4239d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair private void assertValidReplyCode(int replyCode) { 4249d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.isTrue(replyCode > 0, "The number [" + replyCode + "] is not a valid reply code"); 4259d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4269d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4279d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4289d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Return the attribute value for the specified name. Return null if no attribute value 4299d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * exists for that name or if the attribute value is null. 4309d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param name - the attribute name; may not be null 4319d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the value of the attribute stored under name; may be null 4329d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4339d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#getAttribute(java.lang.String) 4349d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4359d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public Object getAttribute(String name) { 4369d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(name, "name"); 4379d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return attributes.get(name); 4389d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4399d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4409d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4419d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Store the value under the specified attribute name. 4429d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param name - the attribute name; may not be null 4439d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param value - the attribute value; may be null 4449d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4459d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#setAttribute(java.lang.String, java.lang.Object) 4469d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4479d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void setAttribute(String name, Object value) { 4489d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(name, "name"); 4499d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair attributes.put(name, value); 4509d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4519d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4529d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4539d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Return the Set of names under which attributes have been stored on this session. 4549d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Returns an empty Set if no attribute values are stored. 4559d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @return the Set of attribute names 4569d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4579d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#getAttributeNames() 4589d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4599d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public Set getAttributeNames() { 4609d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair return attributes.keySet(); 4619d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4629d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4639d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair /** 4649d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * Remove the attribute value for the specified name. Do nothing if no attribute 4659d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * value is stored for the specified name. 4669d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @param name - the attribute name; may not be null 4679d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @throws AssertFailedException - if name is null 4689d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * 4699d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair * @see org.mockftpserver.core.session.Session#removeAttribute(java.lang.String) 4709d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair */ 4719d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair public void removeAttribute(String name) { 4729d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair Assert.notNull(name, "name"); 4739d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair attributes.remove(name); 4749d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair } 4759d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair 4769d9aece7b2c2865253fdd2946a4d11a4f642c5aechrismair} 477