Commit 5e33fbb0 authored by Matt Tucker's avatar Matt Tucker Committed by matt

Refactoring, removing unused classes.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@882 b35dd754-fafc-0310-a699-88a17e54d16e
parent 3d47ee29
......@@ -54,9 +54,6 @@ Choose your database from the list below for setup details:
<a href="http://www.mysql.com/downloads/api-jdbc.html">
http://www.mysql.com/downloads/api-jdbc.html</a>
<p>
If you still have the older mm.mysql JDBC drivers, you are encouraged to
upgrade to the newest Connector/J drivers (3.0.2 or later).
<p>
In the Jive Messenger web-based setup tool, use the following values:<p><ul>
<li>driver: <tt>com.mysql.jdbc.Driver</tt>
<li>server: <tt>jdbc:mysql://[YOUR_HOST]/[DATABASE_NAME]</tt>
......
......@@ -26,7 +26,7 @@ public interface ConnectionManager {
*
* @return Iterator of the ports managed by this connection manager (can be an empty but never null)
*/
public Iterator getPorts();
public Iterator<ServerPort> getPorts();
/**
* <p>Adds a socket to be managed by the connection manager.</p>
......
......@@ -12,6 +12,7 @@
package org.jivesoftware.messenger;
import java.util.Iterator;
import java.util.ArrayList;
/**
* Represents a port on which the server will listen for connections.
......@@ -20,42 +21,70 @@ import java.util.Iterator;
*
* @author Iain Shigeoka
*/
public interface ServerPort {
public class ServerPort {
private int port;
private ArrayList names;
private String address;
private boolean secure;
private String algorithm;
public ServerPort(int port, String name, String address, boolean isSecure,
String algorithm)
{
this.port = port;
this.names = new ArrayList(1);
this.names.add(name);
this.address = address;
this.secure = isSecure;
this.algorithm = algorithm;
}
/**
* Obtain the port number that is being used
* Returns the port number that is being used.
*
* @return The port number this server port is listening on
* @return the port number this server port is listening on.
*/
public int getPort();
public int getPort() {
return port;
}
/**
* Obtain the logical domains for this server port. As multiple
* Returns the logical domains for this server port. As multiple
* domains may point to the same server, this helps to define what
* the server considers "local".
*
* @return The server domain name(s) as Strings
*/
public Iterator getDomainNames();
public Iterator getDomainNames() {
return names.iterator();
}
/**
* Obtains the dot separated IP address for the server.
* Returns the dot separated IP address for the server.
*
* @return The dot separated IP address for the server
*/
public String getIPAddress();
public String getIPAddress() {
return address;
}
/**
* Determines if the connection is secure.
*
* @return True if the connection is secure
*/
public boolean isSecure();
public boolean isSecure() {
return secure;
}
/**
* Obtain the basic protocol/algorithm being used to secure
* Returns the basic protocol/algorithm being used to secure
* the port connections. An example would be "SSL" or "TLS".
*
* @return The protocol used or null if this is not a secure server port
*/
public String getSecurityType();
public String getSecurityType() {
return algorithm;
}
}
......@@ -27,11 +27,12 @@ import org.jivesoftware.messenger.muc.spi.MultiUserChatServerImpl;
import org.jivesoftware.messenger.spi.*;
import org.jivesoftware.messenger.container.Module;
import org.jivesoftware.messenger.container.PluginManager;
import org.jivesoftware.messenger.net.MulticastDNSService;
import org.jivesoftware.util.Version;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.net.MulticastDNSService;
import org.jivesoftware.messenger.net.MulticastDNSService;
import org.dom4j.io.SAXReader;
import org.dom4j.Document;
......@@ -47,16 +48,16 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* <p>The main XMPP server that will load, initialize and start all the server's modules. The server
* is unique in the JVM and could be obtained by using the getInstance() static method
* on the XMPPServer class.
* <p/><p>
* The loaded modules will be initialized and may access through the server other modules. This
* means that the only way for a module to locate another module is through the server. The server
* maintains a list of loaded modules all the time.
* </p><p>
* After starting up all the modules the server will load any available plugin. For more information
* follow this link: {@link org.jivesoftware.messenger.container.PluginManager}
* The main XMPP server that will load, initialize and start all the server's
* modules. The server is unique in the JVM and could be obtained by using the
* {@link #getInstance()} method.<p>
*
* The loaded modules will be initialized and may access through the server other
* modules. This means that the only way for a module to locate another module is
* through the server. The server maintains a list of loaded modules.<p>
*
* After starting up all the modules the server will load any available plugin.
* For more information see: {@link org.jivesoftware.messenger.container.PluginManager}.
* </p>
*
* @author Gaston Dombiak
......@@ -115,8 +116,9 @@ public class XMPPServer {
instance = this;
start();
}
/**
* Obtain a snapshot of the server's status.
* Returns a snapshot of the server's status.
*
* @return the server information current at the time of the method call.
*/
......@@ -135,7 +137,8 @@ public class XMPPServer {
}
/**
* Determines if the given address is local to the server (managed by this server domain).
* Returns true if the given address is local to the server (managed by this
* server domain).
*
* @return true if the address is a local address to this server.
*/
......
......@@ -12,6 +12,7 @@
package org.jivesoftware.messenger.container;
import org.jivesoftware.messenger.JiveGlobals;
import org.jivesoftware.messenger.XMPPServer;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import java.io.File;
......@@ -83,6 +84,8 @@ public class AdminConsolePlugin implements Plugin {
jetty.start();
Log.info("Started admin console on port: " + port);
System.out.println("Admin console listening at http://" +
XMPPServer.getInstance().getServerInfo().getName() + ":" + port);
}
catch (Exception e) {
Log.error("Trouble initializing admin console", e);
......
......@@ -9,7 +9,7 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
package org.jivesoftware.messenger.net;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.XMPPServer;
......
......@@ -33,7 +33,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
private SocketAcceptThread socketThread;
private SSLSocketAcceptThread sslSocketThread;
private ArrayList ports;
private ArrayList<ServerPort> ports;
private AuditManager auditManager;
private SessionManager sessionManager;
......@@ -44,7 +44,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
public ConnectionManagerImpl() {
super("Connection Manager");
ports = new ArrayList(2);
ports = new ArrayList<ServerPort>(2);
}
private void createSocket() {
......@@ -75,7 +75,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
if ("".equals(algorithm) || algorithm == null) {
algorithm = "TLS";
}
ports.add(new ServerPortImpl(sslSocketThread.getPort(), serverName,
ports.add(new ServerPort(sslSocketThread.getPort(), serverName,
localIPAddress, true, algorithm));
sslSocketThread.setDaemon(true);
sslSocketThread.start();
......@@ -91,7 +91,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
// Start plain socket unless it's been disabled.
if (JiveGlobals.getBooleanProperty("xmpp.socket.plain.active", true)) {
socketThread = new SocketAcceptThread(this);
ports.add(new ServerPortImpl(socketThread.getPort(),
ports.add(new ServerPort(socketThread.getPort(),
serverName, localIPAddress, false, null));
socketThread.setDaemon(true);
socketThread.start();
......@@ -102,7 +102,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
}
}
public Iterator getPorts() {
public Iterator<ServerPort> getPorts() {
return ports.iterator();
}
......
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.spi;
import org.jivesoftware.messenger.ServerPort;
import java.util.ArrayList;
import java.util.Iterator;
public class ServerPortImpl implements ServerPort {
private int port;
private ArrayList names;
private String address;
private boolean secure;
private String algorithm;
public ServerPortImpl(int port, String name, String address, boolean isSecure, String algorithm) {
this.port = port;
this.names = new ArrayList(1);
this.names.add(name);
this.address = address;
this.secure = isSecure;
this.algorithm = algorithm;
}
public int getPort() {
return port;
}
public Iterator getDomainNames() {
return names.iterator();
}
public String getIPAddress() {
return address;
}
public boolean isSecure() {
return secure;
}
public String getSecurityType() {
return algorithm;
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import java.net.InetSocketAddress;
import java.util.Iterator;
import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.BasicResultFilter;
/**
* <p>Manages the server sockets that accept incoming connections.</p>
*
* <p>The AcceptManager is responsible for managing AcceptPorts, enforcing
* any AcceptPolicies that are in place, and passing accepted connections
* to the ConnectionManager for management.</p>
*
* @author Iain Shigeoka
*/
public interface AcceptManager {
/**
* <p>Obtain the global accept policy that should be applied to
* sockets.</p>
*
* <p>Connections are accepted or declined by applying this global
* accept policy, then the accept policy for the accept port
* the connection was made. The accept port specific policy (if one
* exists) always overrides the global policy. Thus it is safest to
* deny access to all incoming connections that could be remotely
* considered dangerous in the global policy, and then allow
* the appropriate ones on an per-AcceptPort basis.</p>
*
* @return the global accept policy maintained by the accept manager.
*/
AcceptPolicy getGlobalAcceptPolicy();
/**
* <p>Obtain the number of accept ports being managed.</p>
*
* @return the number of accept ports managed.
*/
int getAcceptPortCount();
/**
* <p>Obtain an iterator over all the AcceptPorts being managed.</p>
*
* @return an iterator of accept ports managed.
*/
Iterator getAcceptPorts();
/**
* <p>Obtain an iterator over the AcceptPorts being managed that
* comply with the given result filter.</p>
*
* @param filter The filter to apply to the port list results
* @return An iterator of accept ports managed after filtering
*/
Iterator getAcceptPorts(BasicResultFilter filter);
/**
* <p>Obtains an accept port by address and port.</p>
*
* <p>If the portAddress is null, and there is more than one
* accept port bound to the same port (e.g. multi-homed hosts
* with different accept ports on each interface), one of
* the matching ports is returned. There is no guarantee
* which will be returned, or if the same or different ports
* will be returned on subsequent calls. If it is important
* to obtain all accept ports bound to a particular port, on any
* interface, it is better to search the list of accept ports
* using the getAcceptPorts() method.
*
* @param portAddress The address of the accept port to remove or null
* for any/all addresses.
* @throws NotFoundException If no matching accept port was found
*/
AcceptPort getAcceptPort(InetSocketAddress portAddress)
throws NotFoundException;
/**
* <p>Creates a new accept port that will be bound to the given port
* address.</p>
*
* <p>Creation of the accept port does not bind the port
* or open it for accepting new connections. Adjust the
* resulting AcceptPort instance in order to configure it's
* initial setup.</p>
*
* @param portAddress The address to set the accept port on or null
* to accept connections on any/all local addresses.
* @return The created accept port
* @throws AlreadyExistsException If an accept port already exists on
* the given address and port
*/
AcceptPort createAcceptPort(InetSocketAddress portAddress)
throws AlreadyExistsException;
/**
* <p>Removes the given accept port.</p>
*
* @param acceptPort The accept port to remove
*/
void deleteAcceptPort(AcceptPort acceptPort);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
/**
* <p>Defines the rules that determine whether a connection should be
* accepted or rejected based exclusively on the initial connection
* parameters.</p>
*
* <p>Accept policies allow rejection of incoming connections based on IP
* address (creating white and black lists), time of day, rate, etc. without
* any knowledge of the underlying protocol. In most cases, a simple white/black
* list of IP addresses in the global policy is sufficient.</p>
*
* @author Iain Shigeoka
*/
public interface AcceptPolicy {
/**
* <p>Evaluate if the given connection should be accepted or rejected.</p>
*
* <p>For AcceptPorts the accept policy should examine it's rules
* for accepting and declining. If there is no corresponding rule
* the global accept policy should be applied. If no rules for the
* global accept policy apply, the connection should be rejected.</p>
*
* @param connection The connection to evaluate
* @return True if the connection should be accepted, false otherwise
*/
boolean evaluate(Connection connection);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import org.jivesoftware.net.AcceptPolicy;
import java.io.IOException;
import java.net.InetSocketAddress;
/**
* <p>Represents and manages an network accept port.</p>
*
* <p>Accept ports know what port/address to bind to, have and verify
* their accept policies, and allow a generic mechanism for opening
* and closing ports for accepting connections. The accept port
* implementation accepts incoming connections but never reads from them.</p>
*
* @author Iain Shigeoka
*/
public interface AcceptPort {
/**
* <p>Determines if this accept port generates natively secure connections.</p>
*
* @return True if the port is secure (e.g. SSL/TLS)
*/
boolean isSecure();
/**
* <p>Sets the security status of this connection.</p>
*
* <p>Essentially determines if an SSLServerSocket should be used.
* Secure accept ports natively create SSL/TLS secured sockets.
* However, since many protocols support SASL, connections that
* are not created securely, can be later secured by negotiating
* TLS.</p>
*
* @param secure True if the port is secure (e.g. SSL/TLS)
*/
void setSecure(boolean secure);
/**
* <p>Obtain the inet address that will be used.</p>
*
* <p>The inet address can be either a particular address representing
* a network interface the accept port binds to, or null if any/all
* interfaces should be used. Although the inet address becomes set
* once the underlying server socket is bound, this method will continue
* to return null if the target is any/all. Use the Connections produced
* by the accept port to determine the local interface any particular
* connection is attached to.</p>
*
* @return The inet address this port is bound to, or null if any/all
*/
InetSocketAddress getInetSocketAddress();
/**
* <p>Sets the inet address that will be used.</p>
*
* <p>The inet address can be either a particular address representing
* a network interface the accept port binds to, or null if any/all
* interfaces should be used. Although the inet address becomes set
* once the underlying server socket is bound, this method will continue
* to return null if the target is any/all. Use the Connections produced
* by the accept port to determine the local interface any particular
* connection is attached to.</p>
*
* <p>If the port is open the port will automatically be
* closed before changing the address. The port will
* NOT be automatically re-opened once the address is changed.</p>
*
* @param address The inet address this port is bound to, or null if any/all
*/
void setInetSocketAddress(InetSocketAddress address);
/**
* <p>Returns the number of milliseconds the accept port has been open
* or -1 if the accept port is closed.</p>
*
* @return The number of milliseconds the accept port has been open or -1 if closed
*/
long getUptime();
/**
* <p>Binds and opens the accept port for accepting new connections.</p>
*
* @throws IOException If the the port is already bound or otherwise failed to open
* @throws SecurityException If the security manager refuses the operation
* @throws IllegalArgumentException If the InetAddress is not valid on this machine
*/
void open()
throws IOException, SecurityException, IllegalArgumentException;
/**
* <p>Closes the accept port and no longer accept connections.</p>
*
* @throws IOException If the the port could not be closed
*/
void close() throws IOException;
/**
* <p>Closes the AcceptPort and removes it from service.</p>
*
* @throws IOException If there was a problem closing the connection
*/
void remove() throws IOException;
/**
* <p>The accept policy specific to this accept port.</p>
*
* <p>Incoming accept requests are evaluated with the AcceptPort's
* AcceptPolicy. The AcceptManager only checks this accept policy
* when deciding whether to connect or reject incoming sockets.
* Most accept port specific accept policies should use the
* results of the global AcceptManager AcceptPolicy
* in determining the overall accept response.</p>
*
* @return The accept policy for the accept port
*/
AcceptPolicy getAcceptPolicy();
/**
* <p>Obtain the monitor watching the basic behavior of this AcceptPort.</p>
*
* <p>Counts the raw connections accepted before the accept policy is applied
* and the sockets are added to the ConnectionManager or they are disconnected
* because they fail to pass the accept policy.</p>
*
* @return The accept monitor watching this accept port
*/
ConnectionMonitor getAcceptMonitor();
/**
* <p>Obtain the monitor watching the accepted connection
* behavior of this AcceptPort.</p>
*
* @return The connection monitor watching this accept port
*/
ConnectionMonitor getConnectMonitor();
/**
* <p>Obtain the monitor watching the rejected connection
* behavior of this AcceptPort.</p>
*
* @return The connection monitor watching this accept port
*/
ConnectionMonitor getDisconnectMonitor();
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import java.net.InetAddress;
import java.util.Date;
/**
* <p>Generic representation of a network connection.</p>
*
* <p>This interface intentionally tries to abstract the underlying socket
* to enable generic use with traditional and nio style sockets.</p>
*
* @author Iain Shigeoka
*/
public interface Connection {
/**
* <p>Obtain the date/time the connection was initially made.</p>
*/
Date getConnectDate();
/**
* <p>Obtain the number of milliseconds the connection has been open.</p>
*
* <p>If the connection is still open, this is the time difference between
* the result of getConnectDate() and the current time
* (e.g. System.currentTimeMillis() otherwise it returns the
* number of milliseconds between the opening and closing of the connection.</p>
*
* @return The number of milliseconds the connection has been open
*/
long getUptime();
/**
* <p>Obtain the InetAddress describing the connection.</p>
*
* @return The InetAddress describing the underlying connection properties
* access this resource
*/
InetAddress getInetAddress();
/**
* <p>Obtain the InetAddress describing the local side of the connection.</p>
*
* @return The InetAddress describing the underlying connection properties
*/
InetAddress getLocalInetAddress();
/**
* <p>Obtain the data consumer that will is used by the connection
* to send outgoing data out of it's underlying socket.</p>
*
* @return The DataConsumer that sends data using this connection
* @throws IllegalStateException If the connection is closed
*/
DataConsumer getDataConsumer() throws IllegalStateException;
/**
* <p>Obtain the data producer that the connection will send
* data read from the connection out on. The producer may
* not have a thread dedicated to it so consumers attached to
* this producer should return from their consume() method as
* quickly as possible.</p>
*
* @return The DataProducer that reads data using this connection
* @throws IllegalStateException If the connection is closed
*/
DataProducer getDataProducer();
/**
* Close this session including associated socket connection.
*
* Any selector registrations (if using nio) are also removed.
* The order of events for closing the session is:
* <ul>
* <li>set closing flag to prevent redundant shutdowns
* <li>notifyEvent all listeners that the channel is shutting down
* <li>close the socket
* </ul>
*/
void close();
/**
* Retrieve the closed state of the Session.
*
* @return True if the session is closed
*/
boolean isClosed();
/**
* Retrieve the mechanism used to open this connection
*
* @return True if the connection was created as a server accept call,
* false otherwise
*/
boolean isAcceptCreated();
/**
* <p>Determines if this connection is secure.</p>
*
* @return True if the connection is secure (e.g. SSL/TLS)
*/
boolean isSecure();
/**
* <p>Sets the security status of this connection.</p>
*
* <p>Ordinarily a connection is either insecure (standard Socket
* or SocketChannel) or secure (SSLSocket). However, many protocols
* including XMPP and NNTP allow the use of SASL where transport
* layer security can be established after the connection is created.
* So application level objects must be able to change the security
* status of a connection during such negotiations.</p>
*
* @param secure True if the connection is secure (e.g. SSL/TLS)
*/
void setSecure(boolean secure);
/**
* Register a listener for close event notification. Registrations after
* the Session is closed will be immediately notified <em>before</em>
* the registration call returns (within the context of the
* registration call). An optional handback object can be associated with
* the registration if the same listener is registered to listen for multiple
* connection closures.
*
* @param listener The listener to register for events
* @param handbackMessage The object to send in the event notification
* @return The message previously registered for this channel or null
* if no registration existed
* @throws UnauthorizedException If caller doesn't have permission to
* access this resource
*/
Object registerCloseListener(ConnectionCloseListener listener,
Object handbackMessage)
throws UnauthorizedException;
/**
* Remove a registered close event listener. Registered listeners must
* be able to receive close events up until the time this method returns.
* (i.e. It is possible to call unregister, receive a close event registration,
* and then have the unregister call return.)
*
* @param listener The listener to deregister for close events
* @return The Message registered with this listener or null if the
* channel was never registered
* @throws UnauthorizedException If caller doesn't have permission
* to access this resource
*/
Object removeCloseListener(ConnectionCloseListener listener)
throws UnauthorizedException;
/**
* <p>Sets the connection manager this connection belongs to.</p>
*
* <p>Connections may only have on connection manager at a time.</p>
*
* @param manager The connection manager for the connection
*/
void setConnectionManager(ConnectionManager manager);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
/**
* Implement and register with a connection to receive notification
* of the connection closing.
*
* @author Iain Shigeoka
*/
public interface ConnectionCloseListener {
/**
* Called when a connection is closed.
*
* @param handback The handback object associated with the connection listener during Connection.registerCloseListener()
*/
public void onConnectionClose(Object handback);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import java.util.Iterator;
import org.jivesoftware.util.BasicResultFilter;
/**
* Manages and maintains server connections.<p>
*
* Beyond simple access to active connections on the server, the
* connection manager can ensure connections are alive, and boot
* connections that are idle according to an idle policy.
*
* @author Iain Shigeoka
*/
public interface ConnectionManager {
int getConnectionCount();
Iterator getConnections();
Iterator getConnections(BasicResultFilter filter);
void addConnection(Connection conn);
void removeConnection(Connection conn);
ConnectionMonitor getConnectedMonitor();
/**
* Connection configuration monitors records any changes in a connection's
* configuration for administration and use in determining runtime behavior.<p>
*
* Samples are the number of connection changes and the rate is measured
* in connections per second. Although some administration events may
* change connection configurations, it is expected that the primary events
* recorded are in protocol-level connection state changes such as the
* establishment of TLS over a previously insecure connection via SASL.
*/
ConnectionMonitor getConfigMonitor();
ConnectionMonitor getDisconnectedMonitor();
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
/**
* <p>ConnectionMonitors record the activity of Connections for administration
* and use in determining runtime behavior.</p>
*
* <p>Samples are the number of connection events and the rate is measured
* in connection events per second.</p>
*
* @author Iain Shigeoka
*/
public interface ConnectionMonitor extends Monitor {
/**
* <p>Adds a connection sample to the monitor as soon as
* the connection is accepted.</p>
*
* @param conn the connection made.
*/
void addSample(Connection conn);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import java.nio.ByteBuffer;
/**
* <p>DataConsumers consume data, optionally passing it along to downstream
* consumers.</p>
*
* <p>Consumers can either obtain their data from a producer using consumeAll()
* or can be pushed data using their consume method. They may also be chained
* by setting a data sink allowing consumers to be chained.</p>
*
* @author Iain Shigeoka
*/
public interface DataConsumer {
/**
* <p>Set a data producer as a source of data when using the consumeAll()
* method.</p>
*
* @param source The producer that will source data to this consumer
* or null for no producer
*/
void setSource(DataProducer source);
/**
* Set a downstream data consumer as a sink for data creating a consumer
* chain.
*
* @param sink The consumer that will consume the data after this consumer
* or null if there is no downstream consumer
*/
void setSink(DataConsumer sink);
/**
* Consume data from the producer until the producer returns null (no data).
*/
void consumeAll();
/**
* Consume the given data.
*
* @param data The data to consume
*/
void consume(ByteBuffer data);
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import org.jivesoftware.net.DataConsumer;
import java.nio.ByteBuffer;
/**
* <p>DataProducers create data to be processed, optionally passing the
* data along to downstream consumers.</p>
*
* <p>Produces can either obtain their data from an upstream producer
* or can be generated (the normal mode). They may also be chained
* by setting a data sink allowing consumers to be chained.</p>
*
* @author Iain Shigeoka
*/
public interface DataProducer {
/**
* <p>Set a data producer as a source of default source of data.</p>
*
* <p>Producers with a source often act as filters for incoming data.</p>
*
* @param source The producer that will source data to this producer
* or null for no upstream producer
*/
void setSource(DataProducer source);
/**
* <p>Set a downstream data consumer as a sink for data.</p>
*
* @param sink The consumer that will consume the data from this producer
* or null if there is no downstream consumer
*/
void setSink(DataConsumer sink);
/**
* <p>Produce data from the producer until the produce() returns null (no data)
* or the sink.consume() returns false.</p>
*/
void produceAll();
/**
* <p>Produce data up to the given limit.</p>
*
* @param limit The limit on the number of bytes to produce or zero for no limit
* @return The bytes produced or an empty buffer no bytes are available
*/
ByteBuffer produce(long limit);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net;
import java.util.Date;
/**
* Monitors a network usage. Monitors measure sample totals, and sample rate
* (samples per second). Monitor information is used at runtime to adjust
* server behavior as well as to generate reports on server history.<p>
*
* Based in part on the DataMonitor from Java Distributed Computing, Jim Farley,
* O'Reilly.
*
* @author Iain Shigeoka
*/
public interface Monitor {
/**
* Add the number of samples that occured between the given start and
* end times.<p>
*
* The monitor does not check for overlapping sample times. It is
* the caller's responsibility to ensure samples don't overlap. Failure
* to prevent overlapping sample times may result in elapsed time
* being falsely counted and reported.
*
* @param quantity The number of samples that occurred during the sample period
* @param startTime The beginning of the sample period
* @param endTime The end of the sample period
*/
void addSample(long quantity, Date startTime, Date endTime);
/**
* <p>Add the number of samples that occured between the last sample date,
* and the current time.</p>
*
* <p>A convenience method when samples occur in sequential periods.
* Equivalent to:</p>
* <pre><code>
* monitor.addSample(quantity, monitor.getLastSampleDate(), new Date());
* </code></pre>
*
* @param quantity The number of samples that occurred between the
* last sample date and now
*/
void addSample(long quantity);
/**
* <p>Obtain the total number of samples reported during the monitor's lifetime.</p>
*
* @return The total number of samples reported to the monitor
*/
long getTotal();
/**
* <p>Obtain the total amount of time (in milliseconds) that the monitor
* has samples for.</p>
*
* @return The total time (in milliseconds) samples have been recorded for
*/
long getTotalTime();
/**
* <p>Obtain the rate of samples reported during the monitor's lifetime.</p>
*
* @return The average rate of samples reported to the monitor
*/
float getRate();
/**
* <p>The date-time of the first sample reported to the monitor.</p>
*
* @return The date-time of the first sample reported to the monitor
*/
Date getFirstSampleDate();
/**
* <p>The date-time of the last sample reported to the monitor.</p>
*
* @return The date-time of the last sample reported to the monitor
*/
Date getLastSampleDate();
/**
* <p>The size of the moving frame (in sample events)
* that provides a recent view of the data.</p>
*
* <p>Samples can be monitored and managed based on
* the usage within the most recent number of samples reported
* during the frame. Larger frame sizes 'smooths' the results
* creating frame statistics that are less affected by outlying samples but
* requiring larger amounts of memory to store and more resources to calculate
* frame statistics.</p>
*
* @return The sample frame size in seconds
*/
int getFrameSize();
/**
* <p>Sets the size of the moving frame (in sample events).</p>
*
* <p>Changing the frame size to a larger value will not automatically
* include past samples that were previously outside the frame. Instead,
* the monitor will not return accurate frame related data until the
* new frame is filled with new data.</p>
*
* <p>Warning: Larger frame sizes consume larger amounts of memory
* per monitor and increases the amount of work required to generate
* frame statistics. Set the framesize to the smallest useful size or zero
* to not record any frame related data.</p>
*
* @param frameSize The new size of the sample frame in seconds
*/
void setFrameSize(int frameSize);
/**
* <p>Obtain the sample total during the frame.</p>
*
* @return The sample total during the frame.
*/
long getFrameTotal();
/**
* <p>Obtain the total sample time during the frame.</p>
*
* @return The total sample time during the frame.
*/
long getFrameTotalTime();
/**
* <p>Obtain the number of bytes read during the frame.</p>
*
* @return The number of bytes read during the frame.
*/
float getFrameRate();
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.policies;
import org.jivesoftware.net.AcceptPolicy;
import org.jivesoftware.net.Connection;
/**
* Performs a basic logical AND evaluation on child policies (e.g. both must
* evaluate to true in order for this policy to evaluate true).<p>
*
* This policy is useful for combining simpler policies to create
* complex policy decisions. The comparison is done using the logical
* AND operation so if the first policy evaluates to false, the second
* policy is not evaluated.
*
* @author Iain Shigeoka
*/
public class AndPolicy implements AcceptPolicy {
private AcceptPolicy policy1;
private AcceptPolicy policy2;
/**
* <p>Create an AND policy with the given two child policies.</p>
*
* @param firstPolicy The first policy that will be evaluated
* @param secondPolicy The first policy that will be evaluated
*/
public AndPolicy(AcceptPolicy firstPolicy, AcceptPolicy secondPolicy){
policy1 = firstPolicy;
policy2 = secondPolicy;
}
public boolean evaluate(Connection connection) {
return policy1.evaluate(connection) && policy2.evaluate(connection);
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.policies;
import org.jivesoftware.net.AcceptPolicy;
import org.jivesoftware.net.Connection;
/**
* The simplest possible accept policy that either accepts or rejects
* all connections.
*
* @author Iain Shigeoka
*/
public class BasicAcceptPolicy implements AcceptPolicy {
private boolean accept;
/**
* Create a basic accept policy that either accepts or denies all
* incoming connections.
*
* @param alwaysAccept True if the policy should accept all connections
*/
public BasicAcceptPolicy(boolean alwaysAccept){
accept = alwaysAccept;
}
public boolean evaluate(Connection connection) {
return accept;
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.policies;
import org.jivesoftware.net.AcceptPolicy;
import org.jivesoftware.net.Connection;
import org.jivesoftware.net.AcceptPolicy;
/**
* Performs a basic logical NOT evaluation on a child policy (e.g.
* turns true to false and false to true).<p>
*
* This policy is useful for combining simpler policies to create
* complex policy decisions.
*
* @author Iain Shigeoka
*/
public class NotPolicy implements AcceptPolicy {
private AcceptPolicy pol;
/**
* Create an NOT policy for the given policy.
*
* @param policy The policy that will be NOT'd
*/
public NotPolicy(AcceptPolicy policy){
pol = policy;
}
public boolean evaluate(Connection connection) {
return !pol.evaluate(connection);
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.policies;
import org.jivesoftware.net.AcceptPolicy;
import org.jivesoftware.net.Connection;
import org.jivesoftware.net.AcceptPolicy;
/**
* Performs a basic logical OR evaluation on child policies (e.g. if either
* evaluate to true this policy will evaluate true).<p>
*
* This policy is useful for combining simpler policies to create
* complex policy decisions. The comparison is done using the logical
* OR operation so if the first policy evaluates to true, the second
* policy is not evaluated.
*
* @author Iain Shigeoka
*/
public class OrPolicy implements AcceptPolicy {
private AcceptPolicy policy1;
private AcceptPolicy policy2;
/**
* <p>Create an OR policy with the given two child policies.</p>
*
* @param firstPolicy The first policy that will be evaluated
* @param secondPolicy The first policy that will be evaluated
*/
public OrPolicy(AcceptPolicy firstPolicy, AcceptPolicy secondPolicy){
policy1 = firstPolicy;
policy2 = secondPolicy;
}
public boolean evaluate(Connection connection) {
return policy1.evaluate(connection) || policy2.evaluate(connection);
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.policies;
import org.jivesoftware.net.AcceptPolicy;
import org.jivesoftware.net.Connection;
import org.jivesoftware.net.AcceptPolicy;
/**
* Performs a bitwise logical XOR (exclusive OR) evaluation on
* child policies (e.g. if both policies evaluate to true or false
* the XOR result is false).<p>
*
* This policy is useful for combining simpler policies to create
* complex policy decisions. The comparison is done using the bitwise
* XOR operation so both policies will always be evaluated.
*
* @author Iain Shigeoka
*/
public class XorPolicy implements AcceptPolicy {
private AcceptPolicy policy1;
private AcceptPolicy policy2;
/**
* Create an AND policy with the given two child policies.
*
* @param firstPolicy The first policy that will be evaluated
* @param secondPolicy The first policy that will be evaluated
*/
public XorPolicy(AcceptPolicy firstPolicy, AcceptPolicy secondPolicy){
policy1 = firstPolicy;
policy2 = secondPolicy;
}
public boolean evaluate(Connection connection) {
return policy1.evaluate(connection) ^ policy2.evaluate(connection);
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.policies.BasicAcceptPolicy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.*;
import org.jivesoftware.net.*;
import org.jivesoftware.messenger.JiveGlobals;
import org.jivesoftware.util.Log;
public class AcceptPortImpl implements AcceptPort {
private String context;
private InetSocketAddress address;
private boolean secure;
private AcceptPolicy policy = new BasicAcceptPolicy(true);
private long startTime = -1;
private ConnectionMonitor acceptMonitor = new TransientConnectionMonitor();
private ConnectionMonitor connectMonitor = new TransientConnectionMonitor();
private ConnectionMonitor disconnectMonitor = new TransientConnectionMonitor();
private AcceptThread acceptThread = null;
private ConnectionManager conManager;
public AcceptPortImpl(String context, ConnectionManager connectionManager) {
this.context = context;
conManager = connectionManager;
String hostname = JiveGlobals.getProperty(context + ".hostname");
int port = JiveGlobals.getIntProperty(context + ".portnumber", -1);
if (port == -1) {
Log.error("No port number set for " + context);
return;
}
if (hostname == null) {
address = new InetSocketAddress(port);
}
else {
address = new InetSocketAddress(hostname, port);
}
}
public AcceptPortImpl(String context, ConnectionManager connectionManager,
InetSocketAddress bindAddress)
{
this.context = context;
conManager = connectionManager;
address = bindAddress;
savePort();
}
public void setContext(String context) {
this.context = context;
}
public boolean isSecure() {
return secure;
}
public void setSecure(boolean securePort) {
this.secure = securePort;
savePort();
}
public InetSocketAddress getInetSocketAddress() {
return address;
}
public void setInetSocketAddress(InetSocketAddress bindAddress) {
this.address = bindAddress;
savePort();
}
public long getUptime() {
long uptime = -1;
if (startTime > 0){
uptime = System.currentTimeMillis() - startTime;
}
return uptime;
}
public void open()
throws IOException, SecurityException, IllegalArgumentException {
if (acceptThread == null){
acceptThread = new AcceptThread();
acceptThread.init();
acceptThread.start();
startTime = System.currentTimeMillis();
}
}
public void close() throws IOException {
if (acceptThread != null){
acceptThread.close();
acceptThread = null;
}
}
public void remove() throws IOException {
JiveGlobals.deleteProperty(context);
if (startTime > 0){
close();
}
}
public AcceptPolicy getAcceptPolicy() {
return policy;
}
public ConnectionMonitor getAcceptMonitor() {
return acceptMonitor;
}
public ConnectionMonitor getConnectMonitor() {
return connectMonitor;
}
public ConnectionMonitor getDisconnectMonitor() {
return disconnectMonitor;
}
public void savePort(){
if (address != null){
JiveGlobals.setProperty(context + ".hostname",address.getHostName());
JiveGlobals.setProperty(context + ".portnumber",Integer.toString(address.getPort()));
}
}
private class AcceptThread extends Thread{
AcceptThread(){
super("Accept thread - " + address.toString());
setDaemon(true);
}
private ServerSocketChannel server;
private boolean running = false;
void init() throws IOException {
server = ServerSocketChannel.open();
server.socket().bind(address);
server.configureBlocking(true);
}
public void run(){
running = true;
while (running){
try {
SocketChannel socket = server.accept();
Connection conn = new SocketChannelConnection(socket);
acceptMonitor.addSample(conn);
if (policy.evaluate(conn)) {
conManager.addConnection(conn);
connectMonitor.addSample(conn);
}
else {
disconnectMonitor.addSample(conn);
}
}
catch (ClosedChannelException ce){
startTime = -1;
running = false;
}
catch (NotYetBoundException ce){
throw new IllegalStateException("Must init thread before running");
}
catch (IOException e) {
Log.error(e);
}
}
}
void close(){
if (running){
running = false;
startTime = -1;
try {
server.close();
}
catch (IOException e) {
//
}
}
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.DataConsumer;
import org.jivesoftware.net.DataProducer;
import org.jivesoftware.net.Monitor;
import java.nio.ByteBuffer;
import java.util.Date;
import org.jivesoftware.net.DataConsumer;
import org.jivesoftware.net.DataProducer;
/**
* <p>A basic implementation of the data consumer .</p>
*
* @author Iain Shigeoka
*/
public class BasicDataConsumer implements DataConsumer {
protected DataProducer source;
protected DataConsumer sink;
protected Monitor monitor;
BasicDataConsumer(DataProducer source, DataConsumer sink, Monitor monitor){
this.source = source;
this.sink = sink;
this.monitor = monitor;
}
BasicDataConsumer(Monitor monitor){
this.monitor = monitor;
}
BasicDataConsumer(){
}
public void setSource(DataProducer source) {
this.source = source;
}
public void setSink(DataConsumer sink) {
this.sink = sink;
}
public void consumeAll() {
if (source != null){
ByteBuffer data = source.produce(0);
while (data != null){
consume(data);
data = source.produce(0);
}
}
}
public void consume(ByteBuffer data) {
Date start = new Date();
int size = data.remaining();
preConsume(data);
if (data.hasRemaining()){
doConsume(data);
}
if (data.hasRemaining()){
postConsume(data);
}
if (monitor != null){
monitor.addSample(size,start,new Date());
}
if (sink != null && data.hasRemaining()){
sink.consume(data);
}
}
protected void preConsume(ByteBuffer data){
}
protected void doConsume(ByteBuffer data){
}
protected void postConsume(ByteBuffer data){
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.DataConsumer;
import org.jivesoftware.net.DataProducer;
import org.jivesoftware.net.Monitor;
import java.nio.ByteBuffer;
import java.util.Date;
import org.jivesoftware.net.DataConsumer;
import org.jivesoftware.net.DataProducer;
/**
* A basic data producer that produces data from it's
* source and adds samples to the monitor.
*
* @author Iain Shigeoka
*/
public class BasicDataProducer implements DataProducer {
/** The source of the data producer, or null if none */
protected DataProducer source;
/** The sink for the data producer data, or null if none */
protected DataConsumer sink;
/** The mnitor of the data producer data, or null if none */
protected Monitor monitor;
/**
* <p>Create a data producer with the given source, sink, and monitor.</p>
*
* @param source The data source or null for none
* @param sink The data sink or null for none
* @param monitor The monitor to use or null for none
*/
public BasicDataProducer(DataProducer source, DataConsumer sink, Monitor monitor){
this.source = source;
this.sink = sink;
this.monitor = monitor;
}
/**
* <p>Create a data producer with the given monitor.</p>
*
* @param monitor The monitor to use or null for none
*/
public BasicDataProducer(Monitor monitor){
this(null,null,monitor);
}
/**
* <p>Create a data producer without any source, sink, and monitor.</p>
*/
public BasicDataProducer(){
this(null,null,null);
}
public void setSource(DataProducer source) {
this.source = source;
}
public void setSink(DataConsumer sink) {
this.sink = sink;
}
public void produceAll() {
if (sink != null){
ByteBuffer data = produce(0);
while(data.hasRemaining()){
sink.consume(data);
data = produce(0);
}
}
}
public ByteBuffer produce(long limit) {
Date start = new Date();
ByteBuffer data = null;
boolean success = preProduction(limit);
if (success){
data = doProduction(limit);
}
if (success && data != null){
success = postProduction(data,limit);
}
if (monitor != null){
monitor.addSample(data.remaining(),start,new Date());
}
if (data != null && sink != null){
sink.consume(data);
}
return data;
}
/**
* <p>Called before production begins to verify production should be done.</p>
*
* <p>Override to check the limit size before allowing actual production.</p>
*
* @param limit The limit to check before data production or zero for no limit
* @return True if the production should continue
*/
protected boolean preProduction(long limit){
return true;
}
/**
* <p>Conduct the actual production for at most <code>limit</code> bytes.</p>
*
* @param limit The limit in bytes to produce
* @return The bytes read or null for no bytes
*/
protected ByteBuffer doProduction(long limit){
ByteBuffer data = null;
if (source != null){
data = source.produce(limit);
}
return data;
}
/**
* <p>Called after production if verification of the read bytes should
* be made before returning.</p>
*
* @param data The data that was read or null if no data read
* @param limit The limit in bytes to produce
* @return True if the production was successful
*/
protected boolean postProduction(ByteBuffer data, long limit){
return true;
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.Monitor;
import org.jivesoftware.util.CircularLinkedList;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Implements a transient (in-memory) generic monitor.
*
* @author Iain Shigeoka
*/
public class BasicTransientMonitor implements Monitor {
private static final int DEFAULT_FRAME_SIZE = 20;
private long totalSamples;
private long totalTime;
private long lastSampleTime = -1;
private long firstSampleTime = -1;
private Date startUpTime;
private int frameSize;
private CircularLinkedList frameList = new CircularLinkedList();
private ReadWriteLock frameLock = new ReentrantReadWriteLock();
public BasicTransientMonitor(){
startUpTime = new Date();
frameSize = DEFAULT_FRAME_SIZE;
}
public void addSample(long quantity, Date startTime, Date endTime) {
addSample(quantity,startTime.getTime(),endTime.getTime());
}
public void addSample(long quantity) {
if (lastSampleTime < 0){
lastSampleTime = startUpTime.getTime();
}
addSample(quantity,lastSampleTime,System.currentTimeMillis());
}
private void addSample(long quantity, long startTime, long endTime){
totalSamples += quantity;
totalTime += endTime - startTime;
lastSampleTime = endTime;
if (firstSampleTime == -1){
firstSampleTime = startTime;
}
frameLock.writeLock().lock();
try {
Sample sample = new Sample(quantity, endTime - startTime);
if (frameList.size() < frameSize){
frameList.add(sample);
} else {
// overwrite oldest sample
// the newest sample is at next() and the oldest at prev()
// so move the pointer to one back using prev() then set that
// value to be the youngest sample so the samples are again
// in their natural order.
frameList.prev();
frameList.setNext(sample);
}
} finally {
frameLock.writeLock().unlock();
}
}
public long getTotal() {
return totalSamples;
}
public long getTotalTime() {
return totalTime;
}
public float getRate() {
return (float)totalSamples / (float)(totalTime * 1000);
}
public Date getFirstSampleDate() {
Date time = startUpTime;
if (firstSampleTime > 0){
time = new Date(firstSampleTime);
}
return time;
}
public Date getLastSampleDate() {
Date time = null;
if (lastSampleTime > 0){
time = new Date(lastSampleTime);
} else {
time = startUpTime;
}
return time;
}
public int getFrameSize() {
return frameSize;
}
public void setFrameSize(int newSize) {
frameLock.writeLock().lock();
try {
while (frameList.size() > newSize){
frameList.prev();
frameList.remove();
}
} finally {
frameLock.writeLock().unlock();
}
frameSize = newSize;
}
public long getFrameTotal() {
long quantity = 0;
if (frameList.size() > 0){
frameLock.writeLock().lock();
try {
frameList.mark();
while (frameList.getPassCount() == 0){
Sample sample = (Sample) frameList.next();
quantity += sample.getQuantity();
}
} finally {
frameLock.writeLock().lock();
}
}
return quantity;
}
public long getFrameTotalTime() {
long time = 0;
if (frameList.size() > 0){
frameLock.writeLock().lock();
try {
frameList.mark();
while (frameList.getPassCount() == 0){
Sample sample = (Sample) frameList.next();
time += sample.getTime();
}
} finally {
frameLock.writeLock().lock();
}
}
return time;
}
public float getFrameRate() {
float time = 0;
float quantity = 0;
if (frameList.size() > 0){
frameLock.writeLock().lock();
try {
frameList.mark();
while (frameList.getPassCount() == 0){
Sample sample = (Sample) frameList.next();
time += sample.getTime();
quantity += sample.getQuantity();
}
} finally {
frameLock.writeLock().lock();
}
}
return quantity / time * 1000;
}
/**
* <p>Represents a single sample event for use in the frame circular list.</p>
*
* @author Iain Shigeoka
*/
private class Sample{
private long q;
private long t;
/**
* <p>Create a sample with given quantity and time duration.</p>
*
* @param quantity The quantity of the sample
* @param time The time in milliseconds the sample took
*/
Sample(long quantity, long time){
q = quantity;
t = time;
}
/**
* <p>Returns the quantity of the sample.</p>
*
* @return Quantity of the sample
*/
long getQuantity(){
return q;
}
/**
* <p>Returns the total time of the sample.</p>
*
* @return Time of the sample in milliseconds
*/
long getTime(){
return t;
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.ConnectionManager;
import org.jivesoftware.net.Connection;
import org.jivesoftware.net.ConnectionMonitor;
import java.util.Iterator;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.util.ConcurrentHashSet;
import org.jivesoftware.util.BasicResultFilter;
public class ConnectionManagerImpl extends BasicModule implements ConnectionManager {
private ConcurrentHashSet connections = new ConcurrentHashSet();
private ConnectionMonitor connectedMonitor = new TransientConnectionMonitor();
private ConnectionMonitor configMonitor = new TransientConnectionMonitor();
private ConnectionMonitor disconnectedMonitor = new TransientConnectionMonitor();
public ConnectionManagerImpl() {
super("Connection Manager");
}
public int getConnectionCount() {
return connections.size();
}
public Iterator getConnections() {
return connections.iterator();
}
public Iterator getConnections(BasicResultFilter filter) {
return filter.filter(connections.iterator());
}
public void addConnection(Connection conn) {
connections.add(conn);
conn.setConnectionManager(this);
connectedMonitor.addSample(conn);
}
public void removeConnection(Connection conn) {
connections.remove(conn);
disconnectedMonitor.addSample(conn);
}
public ConnectionMonitor getConnectedMonitor() {
return connectedMonitor;
}
public ConnectionMonitor getConfigMonitor() {
return configMonitor;
}
public ConnectionMonitor getDisconnectedMonitor() {
return disconnectedMonitor;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import java.util.Date;
import java.net.InetAddress;
import java.nio.channels.SocketChannel;
import org.jivesoftware.net.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
public class SocketChannelConnection implements Connection {
private SocketChannel socket;
public SocketChannelConnection(SocketChannel socket){
this.socket = socket;
}
public Date getConnectDate() {
return null;
}
public long getUptime() {
return 0;
}
public InetAddress getInetAddress() {
return null;
}
public InetAddress getLocalInetAddress() {
return null;
}
public DataConsumer getDataConsumer() throws IllegalStateException {
return null;
}
public DataProducer getDataProducer() {
return null;
}
public void close() {
}
public boolean isClosed() {
return false;
}
public boolean isAcceptCreated() {
return false;
}
public boolean isSecure() {
return false;
}
public void setSecure(boolean secure) {
}
public Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage) throws UnauthorizedException {
return null;
}
public Object removeCloseListener(ConnectionCloseListener listener) throws UnauthorizedException {
return null;
}
public void setConnectionManager(ConnectionManager manager) {
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.Connection;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import org.jivesoftware.net.spi.BasicDataConsumer;
/**
* Socket data consumer implementation designed to work intimately with
* the SocketConnection class.
*
* @author Iain Shigeoka
*/
public class SocketDataConsumer extends BasicDataConsumer {
private Connection conn;
private OutputStream out;
/**
* <p>Create a new socket consumer for the given connection using
* the provided output stream.</p>
*
* @param out The output stream to write to
* @param connection The connection the data consumer belongs to
*/
SocketDataConsumer(OutputStream out, Connection connection){
super();
this.conn = connection;
this.out = out;
}
/**
* <p>Consume the data by writing it to the underlying socket output stream.</p>
*
* <p>Any IOExceptions during the write will return a false result.</p>
*
* @param data The data to write to the connection's output stream
*/
protected void doConsume(ByteBuffer data){
try {
if (!conn.isClosed()){
// TODO: implement write
//out.write(data);
throw new IOException();
}
} catch (IOException e) {
// TODO: implement log
// log
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.Connection;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.jivesoftware.net.spi.BasicDataProducer;
import org.jivesoftware.net.Connection;
/**
* Produces data from the input stream of a socket connection.
*
* @author Iain Shigeoka
*/
public class SocketDataProducer extends BasicDataProducer {
private Connection conn;
private InputStream in;
/**
* <p>Create a new socket producer for the given connection using
* the provided input stream.</p>
*
* @param in The input stream to read from
* @param connection The connection the data producer belongs to
*/
SocketDataProducer(InputStream in, Connection connection){
super();
this.conn = connection;
this.in = in;
}
protected ByteBuffer doProduction(long limit) {
return super.doProduction(limit);
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.net.spi;
import org.jivesoftware.net.Connection;
import org.jivesoftware.net.ConnectionMonitor;
import org.jivesoftware.net.spi.BasicTransientMonitor;
import org.jivesoftware.net.Connection;
/**
* Transient (in-memory) implementation of a connection monitor.
*
* @author Iain Shigeoka
*/
public class TransientConnectionMonitor
extends BasicTransientMonitor
implements ConnectionMonitor {
public void addSample(Connection conn) {
addSample(1);
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.util.Iterator;
import java.util.LinkedList;
/**
* A convenience result filter designed to restrict list results for display in UI.
* The basic result filter implements the most common result filter allowing the specification
* of the start index and maximum number of results to return on any given list.
*
* @author Iain Shigeoka
*/
public class BasicResultFilter {
/**
* Indicates that no result limit should be enforced.
*/
public static final int NO_RESULT_LIMIT = -1;
/**
* Empty constructor creating a default filter with start index 0, and number of
* results unlimited.
*/
public BasicResultFilter() {
}
/**
* Constructor that sets the start index 0, and number of results.
*
* @param startIndex The start index for the results
* @param numResults The maximum number of workgroups to include in the result
*/
public BasicResultFilter(int startIndex, int numResults) {
this.startIndex = startIndex;
this.numResults = numResults;
}
/**
* The starting index for results. Default is 0.
*/
private int startIndex = 0;
/**
* Number of results to return. Default is NO_RESULT_LIMIT
* which means an unlimited number of results.
*/
private int numResults = NO_RESULT_LIMIT;
/**
* Returns the max number of results that should be returned.
* The default value for is NO_RESULT_LIMIT, which means there will be no limit
* on the number of results. This method can be used in combination with
* setStartIndex(int) to perform pagination of results.
*
* @return the max number of results to return or NO_RESULT_LIMIT for no limit.
* @see #setStartIndex(int)
*/
public int getNumResults() {
return numResults;
}
/**
* Sets the limit on the number of results to be returned.
* User NO_RESULT_LIMIT if you don't want to limit the results returned.
*
* @param numResults the number of results to return or NO_RESULT_LIMIT for no limit
*/
public void setNumResults(int numResults) {
if (numResults != NO_RESULT_LIMIT && numResults < 0) {
throw new IllegalArgumentException("numResults cannot be less than 0.");
}
this.numResults = numResults;
}
/**
* Returns the index of the first result to return.
*
* @return the index of the first result which should be returned.
*/
public int getStartIndex() {
return startIndex;
}
/**
* Sets the index of the first result to return. For example, if the start
* index is set to 20, the Iterator returned will start at the 20th result
* in the query. This method can be used in combination with
* setNumResults(int) to perform pagination of results.
*
* @param startIndex the index of the first result to return.
*/
public void setStartIndex(int startIndex) {
if (startIndex < 0) {
throw new IllegalArgumentException("A start index less than 0 is not valid.");
}
this.startIndex = startIndex;
}
/**
* Filters the raw results according to it's current settings and returns an
* iterator over the result.
*
* @param rawResults Iterator over all support group members
* @return Iterator over group members fitting the current filter settings
*/
public synchronized Iterator filter(Iterator rawResults) {
Iterator result = null;
if (startIndex == 0 && numResults == NO_RESULT_LIMIT) {
result = rawResults;
}
else {
LinkedList list = new LinkedList();
while (rawResults.hasNext() && startIndex-- > 0) {
rawResults.next(); // skip over first x entries
}
if (numResults == NO_RESULT_LIMIT) {
while (rawResults.hasNext()) {
list.add(rawResults.next());
}
}
else {
for (int i = 0; rawResults.hasNext() && i < numResults; i++) {
list.add(rawResults.next());
}
}
result = list.iterator();
}
return result;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.awt.*;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
/**
* A utility class that provides methods that are useful for dealing with
* Java Beans.
*
* @author Jive Software
*/
public class BeanUtils {
/**
* Sets the properties of a Java Bean based on the String name/value pairs in
* the specifieed Map. Because this method has to know how to convert a
* String value into the correct type for the bean, only a few bean property
* types are supported. They are: String, boolean, int, long, float, double,
* Color, and Class.<p>
* <p/>
* If key/value pairs exist in the Map that don't correspond to properties
* of the bean, they will be ignored.
*
* @param bean the JavaBean to set properties on.
* @param properties String name/value pairs of the properties to set.
*/
public static void setProperties(Object bean, Map properties) {
try {
// Loop through all the property names in the Map
for (Iterator iter = properties.keySet().iterator(); iter.hasNext();) {
String propName = (String)iter.next();
try {
// Create a property descriptor for the named property. If
// the bean doesn't have the named property, an
// Introspection will be thrown.
PropertyDescriptor descriptor = new PropertyDescriptor(propName, bean.getClass());
// Load the class type of the property.
Class propertyType = descriptor.getPropertyType();
// Get the value of the property by converting it from a
// String to the correct object type.
Object value = decode(propertyType, (String)properties.get(propName));
// Set the value of the bean.
descriptor.getWriteMethod().invoke(bean, new Object[]{value});
}
catch (IntrospectionException ie) {
// Ignore. This exception means that the key in the map
// does not correspond to a property of the bean.
}
}
}
catch (Exception e) {
Log.error(e);
}
}
/**
* Gets the properties from a Java Bean and returns them in a Map of String
* name/value pairs. Because this method has to know how to convert a
* bean property into a String value, only a few bean property
* types are supported. They are: String, boolean, int, long, float, double,
* Color, and Class.
*
* @param bean a Java Bean to get properties from.
* @return a Map of all properties as String name/value pairs.
*/
public static Map getProperties(Object bean) {
Map properties = new HashMap();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
// Loop through all properties of the bean.
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
String[] names = new String[descriptors.length];
for (int i = 0; i < names.length; i++) {
// Determine the property name.
String name = descriptors[i].getName();
Class type = descriptors[i].getPropertyType();
// Decode the property value using the property type and
// encoded String value.
Object value = descriptors[i].getReadMethod().invoke(bean, (Object)null);
// Add to Map, encoding the value as a String.
properties.put(name, encode(value));
}
}
catch (Exception e) {
Log.error(e);
}
return properties;
}
/**
* Returns the PropertyDescriptor array for the specified Java Bean Class.
* The method also does a special check to see of the bean has a BeanInfo
* class that extends the JiveBeanInfo class. If yes, we load the
* PropertyDescriptor array directly from that BeanInfo class rather than
* through the Introspector in order to preserve the desired ordering of
* properties.
*
* @param beanClass the Class of the JavaBean.
* @return the PropertyDescriptor array for the specified Java Bean Class.
*/
public static PropertyDescriptor[] getPropertyDescriptors(Class beanClass)
throws IntrospectionException {
// See if the Java Bean has a BeanInfo class that implements
// JiveBeanInfo. If so, return the PropertyDescriptor from that
// class. This will bypass properties of parent classes, but this is
// the normal behavior of classes that implement JiveBeanInfo.
try {
JiveBeanInfo beanInfo = (JiveBeanInfo)ClassUtils.forName(beanClass.getName() + "BeanInfo").newInstance();
return beanInfo.getPropertyDescriptors();
}
catch (Exception e) {
}
// Otherwise, return the PropertyDescriptors from the Introspector.
return Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
}
/**
* Encodes a bean property value as a String. If the object type is not
* supported, null will be returned.
*
* @param value an Object to encode in a String representation.
*/
private static String encode(Object value) {
if (value instanceof String) {
return (String)value;
}
if (value instanceof Boolean ||
value instanceof Integer ||
value instanceof Long ||
value instanceof Float ||
value instanceof Double) {
return value.toString();
}
if (value instanceof Color) {
Color color = (Color)value;
return color.getRed() + "," + color.getGreen() + "," + color.getBlue();
}
if (value instanceof Class) {
return ((Class)value).getName();
}
return null;
}
/**
* Decodes a String into an object of the specified type. If the object
* type is not supported, null will be returned.
*
* @param type the type of the property.
* @param value the encode String value to decode.
* @return the String value decoded into the specified type.
*/
private static Object decode(Class type, String value) throws Exception {
if (type.getName().equals("java.lang.String")) {
return value;
}
if (type.getName().equals("boolean")) {
return Boolean.valueOf(value);
}
if (type.getName().equals("int")) {
return Integer.valueOf(value);
}
if (type.getName().equals("long")) {
return Long.valueOf(value);
}
if (type.getName().equals("float")) {
return Float.valueOf(value);
}
if (type.getName().equals("double")) {
return Double.valueOf(value);
}
if (type.getName().equals("java.awt.Color")) {
StringTokenizer tokens = new StringTokenizer(value, ",");
int red = Integer.parseInt(tokens.nextToken());
int green = Integer.parseInt(tokens.nextToken());
int blue = Integer.parseInt(tokens.nextToken());
return new Color(red, green, blue);
}
if (type.getName().equals("java.lang.Class")) {
return ClassUtils.forName(value);
}
return null;
}
// This class is not instantiable.
private BeanUtils() {
// do nothing.
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* <p>Simple circular LinkedList allowing the marking of a position, reset of position, and pass count.</p>
* <p>The circular list provides several tools for 'spinning' the circular list.
* The circular list is best thought of as an iterator on the circular list without
* a head or tail. Adds are conducted at the current list position and nodes lack
* indexes. Call mark() to mark the current node as a 'zero' position. When the next
* list item is called, the pass count is incremented when the current position is equal to
* the marked position. This allows users of the circular list to mark and spin around the
* circular list, checking how many times they go around the list.</p>
*
* @author Jive Software
*/
public class CircularLinkedList {
/**
* <p>The current reference to the list.</p>
*/
private LinkedListNode current;
/**
* <p>The marked position on the list.</p>
*/
private LinkedListNode mark;
/**
* <p>The number of times the iteration has passed the mark.</p>
*/
private int passCount;
/**
* <p>Locks access to the current node.</p>
*/
private ReadWriteLock currentLock = new ReentrantReadWriteLock();
/**
* <p>The number of items in the list.</p>
*/
private int size = 0;
/**
* Creates a new linked list.
*/
public CircularLinkedList() {
mark();
}
/**
* Creates a new linked list.
*/
public CircularLinkedList(Object firstItem) {
add(firstItem);
mark();
}
/**
* <p>Obtain the number of times the iteration of the list has passed the mark.</p>
* <p>The passcount is reset using the mark() method.</p>
*
* @return The number of times the list has iterated past the mark
*/
public int getPassCount() {
return passCount;
}
/**
* Resets the pass count to zero and sets the mark to the current list position.
*/
public synchronized void mark() {
passCount = 0;
mark = current;
}
/**
* Add the given item so the following call to next() will return the item.
*
* @param item The item to be added
*/
public synchronized void add(Object item) {
currentLock.writeLock().lock();
try {
if (current == null) {
current = new LinkedListNode(item, null, null);
current.next = current;
current.previous = current;
mark();
}
else {
LinkedListNode node = current.next;
current.next = new LinkedListNode(item, node, current);
}
size++;
}
finally {
currentLock.writeLock().unlock();
}
}
/**
* Sets the object that will be return by next() to the given value.
* If no next value exists, nothing happens (no new items will be added.
*
* @param item The item to be set
*/
public synchronized void setNext(Object item) {
currentLock.writeLock().lock();
try {
if (current != null) {
current.next.object = item;
}
size++;
}
finally {
currentLock.writeLock().unlock();
}
}
/**
* Sets the object that will be return by prev() to the given value.
* If no prev value exists, nothing happens (no new items will be added.
*
* @param item The item to be set
*/
public synchronized void setPrev(Object item) {
currentLock.writeLock().lock();
try {
if (current != null) {
current.object = item;
}
size++;
}
finally {
currentLock.writeLock().unlock();
}
}
/**
* Advances the list to the next item and returns the item
* found or null if no items are in the list.
*
* @return The next item in the list or null if none exists
*/
public Object next() {
Object item = null;
if (current != null) {
currentLock.writeLock().lock();
try {
current = current.next;
item = current.object;
}
finally {
currentLock.writeLock().unlock();
}
}
if (mark == current) {
passCount++;
}
return item;
}
/**
* Moves the list to the previous item and returns the item
* found or null if no items are in the list.
*
* @return The next item in the list or null if none exists
*/
public Object prev() {
Object item = null;
if (current != null) {
currentLock.writeLock().lock();
try {
item = current.object;
current = current.previous;
}
finally {
currentLock.writeLock().unlock();
}
}
if (mark == current) {
passCount--;
}
return item;
}
/**
* Erases all elements in the list and re-initializes it.
*/
public void clear() {
current = null;
mark();
}
/**
* Removes the first item found that matches equals() on the given item.
* If this list does not contain the element, it is unchanged.
* More formally, removes the first element found by calling next() such that
* (item==null ? get(i)==null : item.equals(get(i))) (if such an element is found before
* making a complete trip around the circular list).
*
* @param item The item to remove
*/
public void remove(Object item) {
if (current != null) {
currentLock.writeLock().lock();
try {
if (item == null ? current.object == null : item.equals(current.object)) {
current = null;
mark();
}
else {
for (LinkedListNode node = current.next;
node != current;
node = node.next) {
if (item == null ? node.object == null : item.equals(node.object)) {
node.previous.next = node.next;
node.next.previous = node.previous;
if (node == mark) {
mark = node.next;
}
break;
}
}
}
size--;
}
finally {
currentLock.writeLock().lock();
}
}
}
/**
* Removes the current item (the item last returned by next() or prev()).
*/
public void remove() {
if (current != null) {
currentLock.writeLock().lock();
try {
LinkedListNode node = current.next;
if (node != null) {
node.previous.next = node.next;
node.next.previous = node.previous;
if (node == mark) {
mark = node.next;
}
}
else {
current = null;
}
size--;
}
finally {
currentLock.writeLock().unlock();
}
}
}
/**
* Returns true if the given object is contained in this list.
*
* @param item the item to check for existance
* @return true if the given object is in the list
*/
public boolean contains(Object item) {
boolean in = false;
if (current != null) {
currentLock.readLock().lock();
try {
if (item == null ? current.object == null : item.equals(current.object)) {
in = true;
}
else {
for (LinkedListNode node = current.next;
node != current;
node = node.next) {
if (item == null ? node.object == null : item.equals(node.object)) {
in = true;
break;
}
}
}
}
finally {
currentLock.readLock().unlock();
}
}
return in;
}
/**
* Returns the number of items in the list.
*
* @return the number of items in the list.
*/
public int size() {
return size;
}
/**
* Returns a String representation of the linked list with a comma
* delimited list of all the elements in the list.
*
* @return a String representation of the LinkedList.
*/
public String toString() {
StringBuffer buf = new StringBuffer();
if (current == null) {
buf.append("empty");
}
else {
currentLock.readLock().lock();
try {
buf.append(current.toString());
for (LinkedListNode node = current.next;
node != current;
node = node.next) {
buf.append(", ").append(node.toString());
}
}
finally {
currentLock.readLock().unlock();
}
}
return buf.toString();
}
/**
* <p>Returns true if the circular linked list is empty.</p>
*
* @return true if the list is empty.
*/
public boolean isEmpty() {
return current == null;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Fast way to copy data from one file to another.
* From ORA Java I/O by Elliotte Rusty Harold.
*
* @author Iain Shigeoka
* @see StreamCopier
*/
public class FileCopier {
/**
* Copies the inFile to the outFile.
*
* @param inFile The file to copy from
* @param outFile The file to copy to
* @throws IOException If there was a problem making the copy
*/
public static void copy(String inFile, String outFile) throws IOException {
copy(new File(inFile), new File(outFile));
}
/**
* Copies the inFile to the outFile.
*
* @param inFile The file to copy from
* @param outFile The file to copy to
* @throws IOException If there was a problem making the copy
*/
public static void copy(File inFile, File outFile) throws IOException {
FileInputStream fin = null;
FileOutputStream fout = null;
try {
fin = new FileInputStream(inFile);
fout = new FileOutputStream(outFile);
StreamCopier.copy(fin, fout);
}
finally {
try {
if (fin != null) fin.close();
}
catch (IOException e) {
// do nothing
}
try {
if (fout != null) fout.close();
}
catch (IOException e) {
// do nothing
}
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
/**
* Generates unique IDs.
*
* @author Iain Shigeoka
*/
public class IDFactory {
private String prefix;
private long count;
/**
* <p>Create the ID factory using the given prefix for all generated id's.</p>
*
* @param prefix The unique string prefix to all id's for this factory
*/
public IDFactory(String prefix) {
this.prefix = prefix;
}
/**
* <p>Create the ID factory using no prefix for generated id's.</p>
*/
public IDFactory() {
}
/**
* Obtain the string representation of this id.
*
* @return The string representation of the id
*/
public String getID() {
String id;
if (prefix == null) {
id = Long.toHexString(count++);
}
else {
id = prefix + Long.toHexString(count++);
}
return id;
}
public String toString() {
return getID();
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
/**
* A List type class for long values. The implementation uses an array. If the number
* of elements grows larger than the capacity, the capacity will automatically grow.
*
* @author Matt Tucker
*/
public final class LongList {
long[] elements;
int capacity;
int size;
/**
* Creates a new list of long values with a default capacity of 50.
*/
public LongList() {
this(50);
}
/**
* Creates a new list of long values with a specified initial capacity.
*
* @param initialCapacity a capacity to initialize the list with.
*/
public LongList(int initialCapacity) {
size = 0;
capacity = initialCapacity;
elements = new long[capacity];
}
/**
* Creates a new list of long values with an initial array of elements.
*
* @param longArray an array to create a list from.
*/
public LongList(long[] longArray) {
size = longArray.length;
capacity = longArray.length + 3;
elements = new long[capacity];
System.arraycopy(longArray, 0, elements, 0, size);
}
/**
* Adds a long value to the end of the list.
*
* @param value the value to add to the list.
*/
public void add(long value) {
elements[size] = value;
size++;
// Expand elements array if we need to.
if (size == capacity) {
capacity = capacity * 2;
long[] newElements = new long[capacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
}
/**
* Adds a long value to the list at the specified index.
*
* @param index the index in the list to add the value at.
* @param value the value to add to the list.
*/
public void add(int index, long value) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index " + index + " not valid.");
}
// Shift elements starting at the index forward.
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = value;
size++;
// Expand elements array if we need to.
if (size == capacity) {
capacity = capacity * 2;
long[] newElements = new long[capacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
}
/**
* Removes a value from the list at the specified index.
*
* @param index the index to remove a value at.
*/
public void remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index " + index + " not valid.");
}
size--;
// Shift elements starting at the index backwards.
for (int i = index; i < size; i++) {
elements[i] = elements[i + 1];
}
}
/**
* Returns the long value at the specified index. If the index is not
* valid, an IndexOutOfBoundException will be thrown.
*
* @param index the index of the value to return.
* @return the value at the specified index.
*/
public long get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index " + index + " not valid.");
}
return elements[index];
}
/**
* Returns the index in this list of the first occurrence of the specified value,
* or -1 if this list does not contain this value.
*
* @param value the value to search for.
* @return the index in this list of the first occurrence of the specified
* value, or -1 if this list does not contain this value.
*/
public int indexOf(long value) {
for (int i = 0; i < size; i++) {
if (elements[i] == value) {
return i;
}
}
return -1;
}
/**
* Returns true if the list contains the specified value.
*
* @param value the value to search for.
* @return true if <tt>value</tt> is found in the list.
*/
public boolean contains(long value) {
return indexOf(value) != -1;
}
/**
* Returns the number of elements in the list.
*
* @return the number of elements in the list.
*/
public int size() {
return size;
}
/**
* Returns a new array containing the list elements.
*
* @return an array of the list elements.
*/
public long[] toArray() {
int size = this.size;
long[] newElements = new long[size];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
return newElements;
}
public String toString() {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < this.size; i++) {
buf.append(elements[i]).append(" ");
}
return buf.toString();
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* A simple tree structure for long values. It's nowhere near a complete tree
* implementation since we don't really need one. However, if anyone is
* interested in finishing this class, or optimizing it, that would be
* appreciated.<p>
* <p/>
* The tree uses three arrays to keep the tree structure. It works as in the
* following example:
* <p/>
* <pre>
* 1
* |-- 3
* |-- |--4
* |-- |--6
* |-- 5
* <p/>
* array index: 0 | 1 | 2 | 3 | 4
* <p/>
* key: 1 | 3 | 4 | 5 | 6
* leftChild: 1 | 2 |-1 |-1 |-1
* rightChild -1 | 3 | 4 |-1 |-1
* </pre>
* <p/>
* Where the key array holds key values, and the leftChild and rightChild arrays
* are pointers to other array indices.<p>
* <p/>
* The tree holds a maximum of 65534 nodes. It is not intended to be thread-safe.
* Based on algorithm found in the book "Introduction To Algorithms" by Cormen
* et all, MIT Press, 1997.
*
* @author Matt Tucker
*/
public final class LongTree implements Cacheable, Externalizable {
long[] keys;
//char arrays let us address get about 65K nodes.
char[] leftChildren;
char[] rightSiblings;
// Pointer to next available slot.
char nextIndex = 2;
/**
* Creates a new tree.
*
* @param rootKey the value of the root node of the tree.
* @param initialCapacity the maximum initial capacity of the tree.
*/
public LongTree(long rootKey, int initialCapacity) {
keys = new long[initialCapacity + 1];
leftChildren = new char[initialCapacity + 1];
rightSiblings = new char[initialCapacity + 1];
// New tree, so set the fields to null at root.
keys[1] = rootKey;
leftChildren[1] = 0;
rightSiblings[1] = 0;
}
/**
* Constructor for use with the Externalizable interface. Normal users
* of this class <b>should not</b> call this constructor.
*/
public LongTree() {
// do nothing
}
/**
* Adds a child to the tree.
*
* @param parentKey the parent to add the new value to.
* @param newKey new value to add to the tree.
*/
public void addChild(long parentKey, long newKey) {
// Find record for parent
char parentIndex = findKey(parentKey, (char)1);
if (parentIndex == 0) {
throw new IllegalArgumentException("Parent key " + parentKey +
" not found when adding child " + newKey + ".");
}
// Expand the arrays if we've run out of room.
if (nextIndex == keys.length) {
int oldSize = keys.length;
// Reserve room for new elements.
int newSize = (int)Math.ceil(oldSize * 1.5);
// Grow keys array.
long[] newKeys = new long[newSize];
System.arraycopy(keys, 0, newKeys, 0, oldSize);
keys = newKeys;
// Grow left children array.
char[] newLeftChildren = new char[newSize];
System.arraycopy(leftChildren, 0, newLeftChildren, 0, oldSize);
leftChildren = newLeftChildren;
// Grow right children array.
char[] newRightSiblings = new char[newSize];
System.arraycopy(rightSiblings, 0, newRightSiblings, 0, oldSize);
rightSiblings = newRightSiblings;
}
// Create record for new key.
keys[nextIndex] = newKey;
leftChildren[nextIndex] = 0;
rightSiblings[nextIndex] = 0;
// Adjust references. Check to see if the parent has any children.
if (leftChildren[parentIndex] == 0) {
// No children, therefore make the new key the first child.
leftChildren[parentIndex] = nextIndex;
}
else {
// The parent has children, so find the right-most child.
long siblingIndex = leftChildren[parentIndex];
while (rightSiblings[new Long(siblingIndex).intValue()] != 0) {
siblingIndex = rightSiblings[new Long(siblingIndex).intValue()];
}
// Add the new entry as a sibling of that last child.
rightSiblings[new Long(siblingIndex).intValue()] = nextIndex;
}
// Finally, increment nextIndex so it's ready for next add.
nextIndex++;
}
/**
* Returns a parent of <code>childKey</code>.
*/
public long getParent(long childKey) {
// If the root key was passed in, return -1;
if (keys[1] == childKey) {
return -1;
}
// Otherwise, perform a search to find the parent.
char childIndex = findKey(childKey, (char)1);
if (childIndex == 0) {
return -1;
}
// Adjust the childIndex pointer until we find the left most sibling of
// childKey.
char leftSiblingIndex = getLeftSiblingIndex(childIndex);
while (leftSiblingIndex != 0) {
childIndex = leftSiblingIndex;
leftSiblingIndex = getLeftSiblingIndex(childIndex);
}
// Now, search the leftChildren array until we find the parent of
// childIndex. First, search backwards from childIndex.
for (int i = childIndex - 1; i >= 0; i--) {
if (leftChildren[i] == childIndex) {
return keys[i];
}
}
// Now, search forward from childIndex.
for (int i = childIndex + 1; i <= leftChildren.length; i++) {
if (leftChildren[i] == childIndex) {
return keys[i];
}
}
// We didn't find the parent, so giving up. This shouldn't happen.
return -1;
}
/**
* Returns a child of <code>parentKey</code> at index <code>index</code>.
*/
public long getChild(long parentKey, int index) {
char parentIndex = findKey(parentKey, (char)1);
if (parentIndex == 0) {
return -1;
}
char siblingIndex = leftChildren[parentIndex];
if (siblingIndex == -1) {
return -1;
}
int i = index;
while (i > 0) {
siblingIndex = rightSiblings[siblingIndex];
if (siblingIndex == 0) {
return -1;
}
i--;
}
return keys[siblingIndex];
}
/**
* Returns the number of children of <code>parentKey</code>.
*/
public int getChildCount(long parentKey) {
int count = 0;
char parentIndex = findKey(parentKey, (char)1);
if (parentIndex == 0) {
return 0;
}
char siblingIndex = leftChildren[parentIndex];
while (siblingIndex != 0) {
count++;
siblingIndex = rightSiblings[siblingIndex];
}
return count;
}
/**
* Returns an array of the children of the parentKey, or an empty array
* if there are no children or the parent key is not in the tree.
*
* @param parentKey the parent to get the children of.
* @return the children of parentKey
*/
public long[] getChildren(long parentKey) {
int childCount = getChildCount(parentKey);
if (childCount == 0) {
return new long[0];
}
long[] children = new long[childCount];
int i = 0;
char parentIndex = findKey(parentKey, (char)1);
char siblingIndex = leftChildren[parentIndex];
while (siblingIndex != 0) {
children[i] = keys[siblingIndex];
i++;
siblingIndex = rightSiblings[siblingIndex];
}
return children;
}
/**
* Returns the index of <code>childKey</code> in <code>parentKey</code> or
* -1 if <code>childKey</code> is not a child of <code>parentKey</code>.
*/
public int getIndexOfChild(long parentKey, long childKey) {
int parentIndex = findKey(parentKey, (char)1);
int index = 0;
char siblingIndex = leftChildren[new Long(parentIndex).intValue()];
if (siblingIndex == 0) {
return -1;
}
while (keys[siblingIndex] != childKey) {
index++;
siblingIndex = rightSiblings[siblingIndex];
if (siblingIndex == 0) {
return -1;
}
}
return index;
}
/**
* Returns the depth in the tree that the element can be found at or -1
* if the element is not in the tree. For example, the root element is
* depth 0, direct children of the root element are depth 1, etc.
*
* @param key the key to find the depth for.
* @return the depth of <tt>key</tt> in the tree hiearchy.
*/
public int getDepth(long key) {
int[] depth = {0};
if (findDepth(key, (char)1, depth) == 0) {
return -1;
}
return depth[0];
}
/**
* Returns the keys in the in the tree in depth-first order. For example,
* give the tree:
* <p/>
* <pre>
* 1
* |-- 3
* |-- |-- 4
* |-- |-- |-- 7
* |-- |-- 6
* |-- 5
* </pre>
* <p/>
* Then this method would return the sequence: 1, 3, 4, 7, 6, 5.
*
* @return the keys of the tree in depth-first order.
*/
public long[] getRecursiveKeys() {
char startIndex = 1;
long[] depthKeys = new long[nextIndex - 1];
depthKeys[0] = keys[startIndex];
int cursor = 1;
// Iterate through each sibling, filling the depthKeys array up.
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
cursor = fillDepthKeys(siblingIndex, depthKeys, cursor);
// Move to next sibling
siblingIndex = rightSiblings[siblingIndex];
}
return depthKeys;
}
/**
* Returns the keys in the in the tree in depth-first order. For example,
* give the tree:
* <p/>
* <pre>
* 1
* |-- 3
* |-- |-- 4
* |-- |-- |-- 7
* |-- |-- 6
* |-- 5
* </pre>
* <p/>
* Then this method would return the sequence: 1, 3, 4, 7, 6, 5.
*
* @param parentKey the parent key to get children of.
* @return the keys of the tree in depth-first order.
*/
public long[] getRecursiveChildren(long parentKey) {
char startIndex = findKey(parentKey, (char)1);
long[] depthKeys = new long[nextIndex - 1];
int cursor = 0;
// Iterate through each sibling, filling the depthKeys array up.
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
cursor = fillDepthKeys(siblingIndex, depthKeys, cursor);
// Move to next sibling
siblingIndex = rightSiblings[siblingIndex];
}
// The cursor variable represents how many keys were actually copied
// into the depth key buffer. Create a new array of the correct size.
long[] dKeys = new long[cursor];
for (int i = 0; i < cursor; i++) {
dKeys[i] = depthKeys[i];
}
return dKeys;
}
/**
* Returns true if the tree node is a leaf.
*
* @return true if <code>key</code> has no children.
*/
public boolean isLeaf(long key) {
int keyIndex = findKey(key, (char)1);
if (keyIndex == 0) {
return false;
}
return (leftChildren[keyIndex] == 0);
}
/**
* Returns the keys in the tree.
*/
public long[] keys() {
long[] k = new long[nextIndex - 1];
for (int i = 0; i < k.length; i++) {
k[i] = keys[i];
}
return k;
}
public int getCachedSize() {
int size = 0;
size += CacheSizes.sizeOfObject() * 3;
size += CacheSizes.sizeOfLong() * keys.length;
size += CacheSizes.sizeOfChar() * keys.length * 2;
size += CacheSizes.sizeOfChar();
return size;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(keys);
out.writeObject(leftChildren);
out.writeObject(rightSiblings);
out.writeChar(nextIndex);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.keys = (long[])in.readObject();
this.leftChildren = (char[])in.readObject();
this.rightSiblings = (char[])in.readObject();
this.nextIndex = in.readChar();
}
/**
* Returns the index of the specified value, or 0 if the key could not be
* found. Tail recursion was removed, but not the other recursive step.
* Using a stack instead often isn't even faster under Java.
*
* @param value the key to search for.
* @param startIndex the index in the tree to start searching at. Pass in
* the root index to search the entire tree.
*/
private char findKey(long value, char startIndex) {
if (startIndex == 0) {
return 0;
}
if (keys[startIndex] == value) {
return startIndex;
}
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
char recursiveIndex = findKey(value, siblingIndex);
if (recursiveIndex != 0) {
return recursiveIndex;
}
else {
siblingIndex = rightSiblings[siblingIndex];
}
}
return 0;
}
/**
* Identical to the findKey method, but it also keeps track of the
* depth.
*/
private char findDepth(long value, char startIndex, int[] depth) {
if (startIndex == 0) {
return 0;
}
if (keys[startIndex] == value) {
return startIndex;
}
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
depth[0]++;
char recursiveIndex = findDepth(value, siblingIndex, depth);
if (recursiveIndex != 0) {
return recursiveIndex;
}
else {
depth[0]--;
siblingIndex = rightSiblings[siblingIndex];
}
}
return 0;
}
/**
* Recursive method that fills the depthKeys array with all the child keys in
* the tree in depth first order.
*
* @param startIndex the starting index for the current recursive iteration.
* @param depthKeys the array of depth-first keys that is being filled.
* @param cursor the current index in the depthKeys array.
* @return the new cursor value after a recursive run.
*/
private int fillDepthKeys(char startIndex, long[] depthKeys, int cursor) {
depthKeys[cursor] = keys[startIndex];
cursor++;
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
cursor = fillDepthKeys(siblingIndex, depthKeys, cursor);
// Move to next sibling
siblingIndex = rightSiblings[siblingIndex];
}
return cursor;
}
/**
* Returs the left sibling index of index. There is no easy way to find a
* left sibling. Therefore, we are forced to linearly scan the rightSiblings
* array until we encounter a reference to index. We'll make the assumption
* that entries are added in order since that assumption can yield big
* performance gain if it's true (and no real performance hit otherwise).
*/
private char getLeftSiblingIndex(char index) {
//First, search backwards throw rightSiblings array
for (int i = index - 1; i >= 0; i--) {
if (rightSiblings[i] == index) {
return (char)i;
}
}
//Now, search forwards
for (int i = index + 1; i < rightSiblings.length; i++) {
if (rightSiblings[i] == index) {
return (char)i;
}
}
//No sibling found, give up.
return (char)0;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.Permissions;
/**
* An interface that defines a method to create proxy objects based on an authToken and permissions.
*
* @author Iain Shigeoka
*/
public interface ProxyFactory {
/**
* Creates a new proxy for <tt>obj</tt> using the specified authToken and permissions, or
* returns null if the user doesn't have permission to read the object.
*
* @return a new proxy.
*/
public Object createProxy(Object obj, AuthToken auth, Permissions perms);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Fast way to copy data from one stream to another.
* From ORA Java I/O by Elliotte Rusty Harold.
*
* @author Iain Shigeoka
*/
public class StreamCopier {
/**
* Copies data from an input stream to an output stream
*
* @param in The stream to copy data from
* @param out The stream to copy data to
* @throws IOException if there's trouble during the copy
*/
public static void copy(InputStream in, OutputStream out) throws IOException {
// do not allow other threads to whack on in or out during copy
synchronized (in) {
synchronized (out) {
byte[] buffer = new byte[256];
while (true) {
int bytesRead = in.read(buffer);
if (bytesRead == -1) break;
out.write(buffer, 0, bytesRead);
}
}
}
}
}
......@@ -465,7 +465,7 @@ public class XMLProperties {
}
// Copy new contents to the file.
try {
FileCopier.copy(tempFile, file);
copy(tempFile, file);
}
catch (Exception e) {
Log.error(e);
......@@ -498,13 +498,63 @@ public class XMLProperties {
}
public void setProperties(Map propertyMap) {
/*
Iterator iter = propertyMap.keySet().iterator();
while (iter.hasNext()) {
String propertyName = (String) iter.next();
String propertyValue = (String) propertyMap.get(propertyName);
setProperty(propertyName, propertyValue);
}
}
/**
* Copies the inFile to the outFile.
*
* @param inFile The file to copy from
* @param outFile The file to copy to
* @throws IOException If there was a problem making the copy
*/
private static void copy(File inFile, File outFile) throws IOException {
FileInputStream fin = null;
FileOutputStream fout = null;
try {
fin = new FileInputStream(inFile);
fout = new FileOutputStream(outFile);
copy(fin, fout);
}
finally {
try {
if (fin != null) fin.close();
}
catch (IOException e) {
// do nothing
}
try {
if (fout != null) fout.close();
}
catch (IOException e) {
// do nothing
}
}
}
/**
* Copies data from an input stream to an output stream
*
* @param in The stream to copy data from
* @param out The stream to copy data to
* @throws IOException if there's trouble during the copy
*/
private static void copy(InputStream in, OutputStream out) throws IOException {
// do not allow other threads to whack on in or out during copy
synchronized (in) {
synchronized (out) {
byte[] buffer = new byte[256];
while (true) {
int bytesRead = in.read(buffer);
if (bytesRead == -1) break;
out.write(buffer, 0, bytesRead);
}
}
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 1999-2003 CoolServlets, Inc. All rights reserved.
*
* This software is the proprietary information of CoolServlets, Inc.
* Use is subject to license terms.
*/
package org.jivesoftware.util;
import junit.framework.TestCase;
import java.util.BitSet;
/**
* Test the standard with an emphasis on making it work with our scheduler.
*
* @author Iain Shigeoka
*/
public class BitSetTest extends TestCase {
/**
* Create a test case with a given name.
*
* @param name The name of the test
*/
public BitSetTest (String name){
super(name);
}
/**
* Test storage and retrieval of a bit set
*/
public void testStorage(){
BitSet bits = new BitSet();
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 1999-2003 CoolServlets, Inc. All rights reserved.
*
* This software is the proprietary information of CoolServlets, Inc.
* Use is subject to license terms.
*/
package org.jivesoftware.util;
import junit.framework.TestCase;
/**
* Test the CircularLinkedList with an emphasis on making it work with RoundRobinDispatcher.
*
* @author Iain Shigeoka
*/
public class CircularLinkedListTest extends TestCase {
/**
* Create a test case with a given name.
*
* @param name The name of the test
*/
public CircularLinkedListTest (String name){
super(name);
}
CircularLinkedList list;
Character A = new Character('A');
Character B = new Character('B');
Character C = new Character('C');
protected void setUp() throws Exception {
list = new CircularLinkedList(new Character('A'));
list.add(new Character('B'));
list.next(); // Must setup list of A, B, C. Without this call, it would be A, C, B.
list.add(new Character('C'));
list.next(); // set the counter to A again
}
/**
* Test iteration through a list
*/
public void testNext(){
assertEquals(A,list.next());
assertEquals(B,list.next());
assertEquals(C,list.next());
assertEquals(A,list.next());
}
/**
* <p>Test behavior of the pass count and iteration: what is next() after passcount increments (same or next)?</li>
*/
public void testPassNext(){
while (!list.next().equals(C)){
// wind the list to A
}
list.mark(); // mark list with the next being A
while (list.getPassCount() < 1){
list.next();
}
// One spin around the list should make A the next again
assertEquals(A,list.next());
}
/**
* Tests the ability of the circular list to maintain pass counts
*/
public void testPassCount(){
list.mark();
for (int i = 0; i < 3; i ++){
assertEquals(0,list.getPassCount());
list.next();
}
assertEquals(1,list.getPassCount());
list.mark();
for (int i = 0; i < 10; i ++){
list.next();
}
assertEquals(3,list.getPassCount());
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment