193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair/* 293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Copyright 2007 the original author or authors. 393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * 493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * you may not use this file except in compliance with the License. 693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * You may obtain a copy of the License at 793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * 893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * http://www.apache.org/licenses/LICENSE-2.0 993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * 1093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Unless required by applicable law or agreed to in writing, software 1193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * distributed under the License is distributed on an "AS IS" BASIS, 1293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * See the License for the specific language governing permissions and 1493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * limitations under the License. 1593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 1693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairpackage org.mockftpserver.core.session; 1793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 18dfa40a06dff44f29d8d5e1d3186055ad325fc7b9chrismairimport org.slf4j.Logger; 19dfa40a06dff44f29d8d5e1d3186055ad325fc7b9chrismairimport org.slf4j.LoggerFactory; 20870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.MockFtpServerException; 21870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.command.Command; 22870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.command.CommandHandler; 23870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.command.CommandNames; 24870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.socket.DefaultServerSocketFactory; 25870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.socket.DefaultSocketFactory; 26870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.socket.ServerSocketFactory; 27870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.socket.SocketFactory; 28870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.util.Assert; 29870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismairimport org.mockftpserver.core.util.AssertFailedException; 30870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair 3149deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.BufferedReader; 3249deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.ByteArrayOutputStream; 3349deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.IOException; 3449deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.InputStream; 3549deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.InputStreamReader; 3649deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.OutputStream; 3749deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.PrintWriter; 3849deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.io.Writer; 3993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairimport java.net.InetAddress; 4093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairimport java.net.ServerSocket; 4193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairimport java.net.Socket; 4293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairimport java.net.SocketTimeoutException; 4349deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.ArrayList; 4449deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.HashMap; 4549deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.List; 4649deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.Map; 4749deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.Set; 4849deb463d1cc3132e4aa60bfd4469398c57c1745chrismairimport java.util.StringTokenizer; 4993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 5093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair/** 5193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Default implementation of the {@link Session} interface. 52870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 5393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @author Chris Mair 54870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * @version $Revision$ - $Date$ 5593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 5693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismairpublic class DefaultSession implements Session { 5793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 58dfa40a06dff44f29d8d5e1d3186055ad325fc7b9chrismair private static final Logger LOG = LoggerFactory.getLogger(DefaultSession.class); 59870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair private static final String END_OF_LINE = "\r\n"; 60725fc0ad3d852d13fbbfd31b07ac20fc2a16eec2chrismair protected static final int DEFAULT_CLIENT_DATA_PORT = 21; 61870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair 62725fc0ad3d852d13fbbfd31b07ac20fc2a16eec2chrismair protected SocketFactory socketFactory = new DefaultSocketFactory(); 63725fc0ad3d852d13fbbfd31b07ac20fc2a16eec2chrismair protected ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); 64870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair 6593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private BufferedReader controlConnectionReader; 6693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private Writer controlConnectionWriter; 6793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private Socket controlSocket; 6893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private Socket dataSocket; 6993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair ServerSocket passiveModeDataSocket; // non-private for testing 7093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private InputStream dataInputStream; 7193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private OutputStream dataOutputStream; 7293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private Map commandHandlers; 7393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private int clientDataPort = DEFAULT_CLIENT_DATA_PORT; 7493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private InetAddress clientHost; 7593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private InetAddress serverHost; 7693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private Map attributes = new HashMap(); 7793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private volatile boolean terminate = false; 7893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 7993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 8093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Create a new initialized instance 81870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 82870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * @param controlSocket - the control connection socket 8393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param commandHandlers - the Map of command name -> CommandHandler. It is assumed that the 84870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * command names are all normalized to upper case. See {@link Command#normalizeName(String)}. 8593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 8693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public DefaultSession(Socket controlSocket, Map commandHandlers) { 8793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(controlSocket, "controlSocket"); 8893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(commandHandlers, "commandHandlers"); 8993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 9093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.controlSocket = controlSocket; 9193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.commandHandlers = commandHandlers; 9293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.serverHost = controlSocket.getLocalAddress(); 9393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 9493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 9593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 9693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Return the InetAddress representing the client host for this session 97870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 9893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the client host 9993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#getClientHost() 10093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 10193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public InetAddress getClientHost() { 10293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return controlSocket.getInetAddress(); 10393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 10493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 10593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 10693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Return the InetAddress representing the server host for this session 107870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 10893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the server host 10993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#getServerHost() 11093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 11193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public InetAddress getServerHost() { 11293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return serverHost; 11393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 11493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 11593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 11693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Send the specified reply code and text across the control connection. 11793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * The reply text is trimmed before being sent. 118870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 11993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param code - the reply code 12093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param text - the reply text to send; may be null 12193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 12293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void sendReply(int code, String text) { 12393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair assertValidReplyCode(code); 12493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 12593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair StringBuffer buffer = new StringBuffer(Integer.toString(code)); 126870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair 12793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (text != null && text.length() > 0) { 12893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair String replyText = text.trim(); 12993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (replyText.indexOf("\n") != -1) { 13093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair int lastIndex = replyText.lastIndexOf("\n"); 13193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair buffer.append("-"); 13293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair for (int i = 0; i < replyText.length(); i++) { 13393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair char c = replyText.charAt(i); 13493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair buffer.append(c); 13593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (i == lastIndex) { 13636a506bc75689778c2cf5a3898d9227f49f9a6c9chrismair buffer.append(Integer.toString(code)); 13736a506bc75689778c2cf5a3898d9227f49f9a6c9chrismair buffer.append(" "); 13893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 13993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 140870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair } else { 14193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair buffer.append(" "); 14293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair buffer.append(replyText); 14393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 14493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 14593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Sending Reply [" + buffer.toString() + "]"); 14693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair writeLineToControlConnection(buffer.toString()); 14793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 14893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 14993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 15093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#openDataConnection() 15193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 15293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void openDataConnection() { 15393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 15493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (passiveModeDataSocket != null) { 15593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Waiting for (passive mode) client connection from client host [" + clientHost 15693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair + "] on port " + passiveModeDataSocket.getLocalPort()); 15793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair // TODO set socket timeout 15893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 15993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataSocket = passiveModeDataSocket.accept(); 16093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Successful (passive mode) client connection to port " 16193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair + passiveModeDataSocket.getLocalPort()); 16293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 16393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (SocketTimeoutException e) { 16493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 16593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 166870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair } else { 16793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(clientHost, "clientHost"); 16893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Connecting to client host [" + clientHost + "] on data port [" + clientDataPort 16993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair + "]"); 17093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataSocket = socketFactory.createSocket(clientHost, clientDataPort); 17193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 17293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataOutputStream = dataSocket.getOutputStream(); 17393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataInputStream = dataSocket.getInputStream(); 17493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 17593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 17693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 17793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 17893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 17993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 18093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 18193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Switch to passive mode 182870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 18393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the local port to be connected to by clients for data transfers 18493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#switchToPassiveMode() 18593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 18693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public int switchToPassiveMode() { 18793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 18893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair passiveModeDataSocket = serverSocketFactory.createServerSocket(0); 18993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return passiveModeDataSocket.getLocalPort(); 19093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 19193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 19293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException("Error opening passive mode server data socket", e); 19393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 19493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 19593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 19693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 19793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#closeDataConnection() 19893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 19993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void closeDataConnection() { 20093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 20193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Flushing and closing client data socket"); 20293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataOutputStream.flush(); 20393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataOutputStream.close(); 20493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataInputStream.close(); 20593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataSocket.close(); 20693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 20793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 20893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.error("Error closing client data socket", e); 20993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 21093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 21193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 21293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 21393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Write a single line to the control connection, appending a newline 214870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 21593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param line - the line to write 21693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 21793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private void writeLineToControlConnection(String line) { 21893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 219870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair controlConnectionWriter.write(line + END_OF_LINE); 22093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair controlConnectionWriter.flush(); 22193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 22293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 22393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.error("Error writing to control connection", e); 22493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException("Error writing to control connection", e); 22593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 22693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 22793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 22893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 22993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#close() 23093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 23193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void close() { 23293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.trace("close()"); 23393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair terminate = true; 23493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 23593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 23693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 23793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#sendData(byte[], int) 23893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 23993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void sendData(byte[] data, int numBytes) { 24093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(data, "data"); 24193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 24293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair dataOutputStream.write(data, 0, numBytes); 24393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 24493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 24593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 24693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 24793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 24893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 24993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 25093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#readData() 25193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 25293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public byte[] readData() { 25363789083e958081738bc2208fa5abdc5a4ef2084chrismair return readData(Integer.MAX_VALUE); 25463789083e958081738bc2208fa5abdc5a4ef2084chrismair } 25593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 25663789083e958081738bc2208fa5abdc5a4ef2084chrismair /** 25763789083e958081738bc2208fa5abdc5a4ef2084chrismair * @see org.mockftpserver.core.session.Session#readData() 25863789083e958081738bc2208fa5abdc5a4ef2084chrismair */ 25963789083e958081738bc2208fa5abdc5a4ef2084chrismair public byte[] readData(int numBytes) { 26093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 26163789083e958081738bc2208fa5abdc5a4ef2084chrismair int numBytesRead = 0; 26293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 26363789083e958081738bc2208fa5abdc5a4ef2084chrismair while (numBytesRead < numBytes) { 26493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair int b = dataInputStream.read(); 26593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (b == -1) { 26693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair break; 26793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 26893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair bytes.write(b); 26963789083e958081738bc2208fa5abdc5a4ef2084chrismair numBytesRead++; 27093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 27193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return bytes.toByteArray(); 27293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 27393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 27493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 27593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 27693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 27793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 27893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 27993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Wait for and read the command sent from the client on the control connection. 280870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 28193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the Command sent from the client; may be null if the session has been closed 282870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * <p/> 283870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * Package-private to enable testing 28493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 28593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Command readCommand() { 28693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 28712674b20efcdd79e793d4ca3c7697232658aa036chrismair final long socketReadIntervalMilliseconds = 20L; 28893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 28993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 29093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair while (true) { 29193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (terminate) { 29293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return null; 29393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 29493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair // Don't block; only read command when it is available 29593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (controlConnectionReader.ready()) { 29693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair String command = controlConnectionReader.readLine(); 29793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.info("Received command: [" + command + "]"); 29893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return parseCommand(command); 29993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 30093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 30193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Thread.sleep(socketReadIntervalMilliseconds); 30293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 30393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (InterruptedException e) { 30493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 30593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 30693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 30793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 30893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 30993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.error("Read failed", e); 31093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 31193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 31293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 31393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 31493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 31593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Parse the command String into a Command object 316870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 31793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param commandString - the command String 31893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the Command object parsed from the command String 31993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 32093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Command parseCommand(String commandString) { 32193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNullOrEmpty(commandString, "commandString"); 32293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 32393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair List parameters = new ArrayList(); 32493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair String name; 32593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 32693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair int indexOfFirstSpace = commandString.indexOf(" "); 32793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (indexOfFirstSpace != -1) { 32893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair name = commandString.substring(0, indexOfFirstSpace); 32993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair StringTokenizer tokenizer = new StringTokenizer(commandString.substring(indexOfFirstSpace + 1), 33093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair ","); 33193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair while (tokenizer.hasMoreTokens()) { 33293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair parameters.add(tokenizer.nextToken()); 33393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 334870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair } else { 33593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair name = commandString; 33693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 33793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 33893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair String[] parametersArray = new String[parameters.size()]; 33993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return new Command(name, (String[]) parameters.toArray(parametersArray)); 34093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 34193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 34293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 34393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#setClientDataHost(java.net.InetAddress) 34493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 34593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void setClientDataHost(InetAddress clientHost) { 34693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.clientHost = clientHost; 34793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 34893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 34993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 35093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#setClientDataPort(int) 35193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 35293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void setClientDataPort(int dataPort) { 35393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.clientDataPort = dataPort; 35493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 35593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair // Clear out any passive data connection mode information 35693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (passiveModeDataSocket != null) { 35793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 35893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair this.passiveModeDataSocket.close(); 35993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 36093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 36193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 36293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 36393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair passiveModeDataSocket = null; 36493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 36593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 36693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 36793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 36893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see java.lang.Runnable#run() 36993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 37093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void run() { 37193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 37293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 37393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair InputStream inputStream = controlSocket.getInputStream(); 37493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair OutputStream outputStream = controlSocket.getOutputStream(); 37593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair controlConnectionReader = new BufferedReader(new InputStreamReader(inputStream)); 37693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair controlConnectionWriter = new PrintWriter(outputStream, true); 37793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 37893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Starting the session..."); 379870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair 38093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair CommandHandler connectCommandHandler = (CommandHandler) commandHandlers.get(CommandNames.CONNECT); 38193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair connectCommandHandler.handleCommand(new Command(CommandNames.CONNECT, new String[0]), this); 38293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 38393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair while (!terminate) { 38493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair readAndProcessCommand(); 38593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 38693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 38793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (Exception e) { 388dfa40a06dff44f29d8d5e1d3186055ad325fc7b9chrismair LOG.error("Error:", e); 38993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair throw new MockFtpServerException(e); 39093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 39193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair finally { 39293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Cleaning up the session"); 39393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair try { 39493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair controlConnectionReader.close(); 39593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair controlConnectionWriter.close(); 39693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 39793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair catch (IOException e) { 398dfa40a06dff44f29d8d5e1d3186055ad325fc7b9chrismair LOG.error("Error:", e); 39993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 40093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair LOG.debug("Session stopped."); 40193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 40293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 40393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 40493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 40593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Read and process the next command from the control connection 406870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 40736a506bc75689778c2cf5a3898d9227f49f9a6c9chrismair * @throws Exception - if any error occurs 40893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 40993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private void readAndProcessCommand() throws Exception { 41093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 41193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Command command = readCommand(); 41293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair if (command != null) { 41393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair String normalizedCommandName = Command.normalizeName(command.getName()); 41493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair CommandHandler commandHandler = (CommandHandler) commandHandlers.get(normalizedCommandName); 41549deb463d1cc3132e4aa60bfd4469398c57c1745chrismair 41649deb463d1cc3132e4aa60bfd4469398c57c1745chrismair if (commandHandler == null) { 41749deb463d1cc3132e4aa60bfd4469398c57c1745chrismair commandHandler = (CommandHandler) commandHandlers.get(CommandNames.UNSUPPORTED); 41849deb463d1cc3132e4aa60bfd4469398c57c1745chrismair } 41949deb463d1cc3132e4aa60bfd4469398c57c1745chrismair 42093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(commandHandler, "CommandHandler for command [" + normalizedCommandName + "]"); 42193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair commandHandler.handleCommand(command, this); 42293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 42393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 42493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 42593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 42693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Assert that the specified number is a valid reply code 427870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 42893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param replyCode - the reply code to check 42993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 43093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair private void assertValidReplyCode(int replyCode) { 43193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.isTrue(replyCode > 0, "The number [" + replyCode + "] is not a valid reply code"); 43293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 43393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 43493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 43593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Return the attribute value for the specified name. Return null if no attribute value 43693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * exists for that name or if the attribute value is null. 437870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 43893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param name - the attribute name; may not be null 43993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the value of the attribute stored under name; may be null 44093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#getAttribute(java.lang.String) 44193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 44293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public Object getAttribute(String name) { 44393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(name, "name"); 44493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return attributes.get(name); 44593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 44693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 44793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 44893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Store the value under the specified attribute name. 449870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 450870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * @param name - the attribute name; may not be null 45193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param value - the attribute value; may be null 45293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#setAttribute(java.lang.String, java.lang.Object) 45393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 45493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void setAttribute(String name, Object value) { 45593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(name, "name"); 45693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair attributes.put(name, value); 45793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 45893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 45993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 46093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Return the Set of names under which attributes have been stored on this session. 46193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Returns an empty Set if no attribute values are stored. 462870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 46393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @return the Set of attribute names 46493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#getAttributeNames() 46593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 46693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public Set getAttributeNames() { 46793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair return attributes.keySet(); 46893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 46993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 47093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair /** 47193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * Remove the attribute value for the specified name. Do nothing if no attribute 47293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * value is stored for the specified name. 473870d738ae5b29b07bdf28c01e1ce55ad30dcecdcchrismair * 47493102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @param name - the attribute name; may not be null 47593102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @throws AssertFailedException - if name is null 47693102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair * @see org.mockftpserver.core.session.Session#removeAttribute(java.lang.String) 47793102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair */ 47893102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair public void removeAttribute(String name) { 47993102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair Assert.notNull(name, "name"); 48093102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair attributes.remove(name); 48193102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair } 48293102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair 48393102446a7b7c3d17888064b4e2e4e5cb534e6d0chrismair} 484