Commit 957c54e4 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Refactoring wortk. JM-924

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@6534 b35dd754-fafc-0310-a699-88a17e54d16e
parent aa39b39a
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 3174 $
* $Date: 2005-12-08 17:41:00 -0300 (Thu, 08 Dec 2005) $
*
* 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.wildfire;
import org.jivesoftware.wildfire.auth.AuthToken;
import org.xmpp.packet.JID;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
/**
* The session represents a connection between the server and a client (c2s) or
* another server (s2s) as well as a connection with a component. Authentication and
* user accounts are associated with c2s connections while s2s has an optional authentication
* association but no single user user.<p>
*
* Obtain object managers from the session in order to access server resources.
*
* @author Gaston Dombiak
*/
public abstract class Session implements RoutableChannelHandler {
/**
* Version of the XMPP spec supported as MAJOR_VERSION.MINOR_VERSION (e.g. 1.0).
*/
public static final int MAJOR_VERSION = 1;
public static final int MINOR_VERSION = 0;
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
protected static String CHARSET = "UTF-8";
public static final int STATUS_CLOSED = -1;
public static final int STATUS_CONNECTED = 1;
public static final int STATUS_STREAMING = 2;
public static final int STATUS_AUTHENTICATED = 3;
/**
* The Address this session is authenticated as.
*/
private JID address;
/**
* The stream id for this session (random and unique).
*/
private StreamID streamID;
/**
* The current session status.
*/
protected int status = STATUS_CONNECTED;
/**
* The connection that this session represents.
*/
protected Connection conn;
/**
* The authentication token for this session.
*/
protected AuthToken authToken;
protected SessionManager sessionManager;
private String serverName;
private Date startDate = new Date();
private long lastActiveDate;
private long clientPacketCount = 0;
private long serverPacketCount = 0;
/**
* Session temporary data. All data stored in this <code>Map</code> disapear when session
* finishes.
*/
private Map<String, Object> sessionData = null;
/**
* Creates a session with an underlying connection and permission protection.
*
* @param connection The connection we are proxying
*/
public Session(String serverName, Connection connection, StreamID streamID) {
conn = connection;
this.streamID = streamID;
this.serverName = serverName;
String id = streamID.getID();
this.address = new JID(null, serverName, id);
this.sessionManager = SessionManager.getInstance();
sessionData = new TreeMap<String, Object>();
}
/**
* Obtain the address of the user. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*
* @return the address of the packet handler.
*/
public JID getAddress() {
return address;
}
/**
* Sets the new address of this session. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*/
public void setAddress(JID address){
this.address = address;
}
/**
* Returns the connection associated with this Session.
*
* @return The connection for this session
*/
public Connection getConnection() {
return conn;
}
/**
* Obtain the current status of this session.
*
* @return The status code for this session
*/
public int getStatus() {
return status;
}
/**
* Set the new status of this session. Setting a status may trigger
* certain events to occur (setting a closed status will close this
* session).
*
* @param status The new status code for this session
*/
public void setStatus(int status) {
this.status = status;
}
/**
* Obtain the stream ID associated with this sesison. Stream ID's are generated by the server
* and should be unique and random.
*
* @return This session's assigned stream ID
*/
public StreamID getStreamID() {
return streamID;
}
/**
* Obtain the name of the server this session belongs to.
*
* @return the server name.
*/
public String getServerName() {
return serverName;
}
/**
* Obtain the date the session was created.
*
* @return the session's creation date.
*/
public Date getCreationDate() {
return startDate;
}
/**
* Obtain the time the session last had activity.
*
* @return The last time the session received activity.
*/
public Date getLastActiveDate() {
return new Date(lastActiveDate);
}
/**
* Increments the number of packets sent from the client to the server.
*/
public void incrementClientPacketCount() {
clientPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Increments the number of packets sent from the server to the client.
*/
public void incrementServerPacketCount() {
serverPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Obtain the number of packets sent from the client to the server.
*
* @return The number of packets sent from the client to the server.
*/
public long getNumClientPackets() {
return clientPacketCount;
}
/**
* Obtain the number of packets sent from the server to the client.
*
* @return The number of packets sent from the server to the client.
*/
public long getNumServerPackets() {
return serverPacketCount;
}
/**
* Saves given session data. Data are saved to temporary storage only and are accessible during
* this session life only and only from this session instance.
*
* @param key a <code>String</code> value of stored data key ID.
* @param value a <code>Object</code> value of data stored in session.
* @see #getSessionData(String)
*/
public void setSessionData(String key, Object value) {
sessionData.put(key, value);
}
/**
* Retrieves session data. This method gives access to temporary session data only. You can
* retrieve earlier saved data giving key ID to receive needed value. Please see
* {@link #setSessionData(String, Object)} description for more details.
*
* @param key a <code>String</code> value of stored data ID.
* @return a <code>Object</code> value of data for given key.
* @see #setSessionData(String, Object)
*/
public Object getSessionData(String key) {
return sessionData.get(key);
}
/**
* Removes session data. Please see {@link #setSessionData(String, Object)} description
* for more details.
*
* @param key a <code>String</code> value of stored data ID.
* @see #setSessionData(String, Object)
*/
public void removeSessionData(String key) {
sessionData.remove(key);
}
/**
* Returns a text with the available stream features. Each subclass may return different
* values depending whether the session has been authenticated or not.
*
* @return a text with the available stream features or <tt>null</tt> to add nothing.
*/
public abstract String getAvailableStreamFeatures();
public String toString() {
return super.toString() + " status: " + status + " address: " + address + " id: " + streamID;
}
protected static int[] decodeVersion(String version) {
int[] answer = new int[] {0 , 0};
String [] versionString = version.split("\\.");
answer[0] = Integer.parseInt(versionString[0]);
answer[1] = Integer.parseInt(versionString[1]);
return answer;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 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.wildfire.net;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.ConnectionManager;
import org.jivesoftware.wildfire.ServerPort;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* Accepts new socket connections using a non-blocking model. A single selector is
* used for all connected clients and also for accepting new connections.
*
* @author Daniele Piras
*/
class NonBlockingAcceptingMode extends SocketAcceptingMode {
// Time (in ms) to sleep from a reading-cycle to another
private static final long CYCLE_TIME = 10;
// Selector to collect messages from client connections.
private Selector selector;
protected NonBlockingAcceptingMode(ConnectionManager connManager, ServerPort serverPort,
InetAddress bindInterface) throws IOException {
super(connManager, serverPort);
// Chaning server to use NIO
// Open selector...
selector = Selector.open();
// Create a new ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// Retrieve socket and bind socket with specified address
this.serverSocket = serverSocketChannel.socket();
this.serverSocket.bind(new InetSocketAddress(bindInterface, serverPort.getPort()));
// Configure Blocking to unblocking
serverSocketChannel.configureBlocking(false);
// Registering connection with selector.
SelectionKey sk = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
AcceptConnection acceptConnection = new AcceptConnection();
sk.attach(acceptConnection);
}
/**
* DANIELE:
* This thread use the selector NIO features to retrieve client connections
* and messages.
*/
public void run() {
while (notTerminated && !Thread.interrupted()) {
try {
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
it.remove();
SelectorAction action = (SelectorAction) key.attachment();
if (action == null) {
continue;
}
if (key.isAcceptable()) {
action.connect(key);
}
else if (key.isReadable()) {
action.read(key);
}
}
Thread.sleep(CYCLE_TIME);
}
catch (IOException ie) {
if (notTerminated) {
Log.error(LocaleUtils.getLocalizedString("admin.error.accept"),
ie);
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.accept"), e);
}
}
}
/*
* InnerClass that is use when a new client arrive.
* It's use the reactor pattern to register an abstract action
* to the selector.
*/
class AcceptConnection implements SelectorAction {
public void read(SelectionKey key) throws IOException {
}
/*
* A client arrive...
*/
public void connect(SelectionKey key) throws IOException {
// Retrieve the server socket channel...
ServerSocketChannel sChannel = (ServerSocketChannel) key.channel();
// Accept the connection
SocketChannel socketChannel = sChannel.accept();
// Retrieve socket for incoming connection
Socket sock = socketChannel.socket();
socketChannel.configureBlocking(false);
// Registering READING operation into the selector
SelectionKey sockKey = socketChannel.register(selector, SelectionKey.OP_READ);
if (sock != null) {
System.out.println("Connect " + sock.toString());
Log.debug("Connect " + sock.toString());
try {
SocketReader reader =
connManager.createSocketReader(sock, false, serverPort, false);
SelectorAction action = new ReadAction(reader);
sockKey.attach(action);
}
catch (Exception e) {
// There is an exception...
Log.error(LocaleUtils.getLocalizedString("admin.error.accept"), e);
}
}
}
}
class ReadAction implements SelectorAction {
SocketReader reader;
public ReadAction(SocketReader reader) {
this.reader = reader;
}
public void read(SelectionKey key) throws IOException {
// Socket reader (using non-blocking mode) will read the stream and process, in
// another thread, any number of stanzas found in the stream.
reader.run();
}
public void connect(SelectionKey key) throws IOException {
}
}
}
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 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.wildfire.net;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* This is a Light-Weight XML Parser.
* It read data from a channel and collect data until data are available in
* the channel.
* When a message is complete you can retrieve messages invoking the method
* getMsgs() and you can invoke the method areThereMsgs() to know if at least
* an message is presents.
*
* @author Daniele Piras
*
*/
class XMLLightweightParser
{
// Chars that rappresent CDATA section start
protected static char[] CDATA_START = {'<','!','[','C','D','A','T','A','['};
// Chars that rappresent CDATA section end
protected static char[] CDATA_END = {']',']','>'};
// Buffer with all data retrieved
protected StringBuilder buffer = new StringBuilder();
// ---- INTERNAL STATUS -------
// Initial status
protected static final int INIT = 0;
// Status used when the first tag name is retrieved
protected static final int HEAD = 2;
// Status used when robot is inside the xml and it looking for the tag conclusion
protected static final int INSIDE = 3;
// Status used when a '<' is found and try to find the conclusion tag.
protected static final int PRETAIL = 4;
// Status used when the ending tag is equal to the head tag
protected static final int TAIL = 5;
// Status used when robot is inside the main tag and found an '/' to check '/>'.
protected static final int VERIFY_CLOSE_TAG = 6;
// Status used when you are inside a parameter
protected static final int INSIDE_PARAM_VALUE = 7;
// Status used when you are inside a cdata section
protected static final int INSIDE_CDATA = 8;
// Current robot status
protected int status = INIT;
// Index to looking for a CDATA section start or end.
protected int cdataOffset = 0;
// Number of chars that machs with the head tag. If the tailCount is equal to
// the head length so a close tag is found.
protected int tailCount = 0;
// Indicate the starting point in the buffer for the next message.
protected int startLastMsg = 0;
// Flag used to discover tag in the form <tag />.
protected boolean insideRootTag = false;
// Object conteining the head tag
protected StringBuilder head = new StringBuilder( 5 );
// List with all finished messages found.
protected List<String> msgs = new ArrayList<String>();
private ReadableByteChannel inputChannel;
byte[] rawByteBuffer;
ByteBuffer byteBuffer;
Charset encoder;
public ReadableByteChannel getChannel()
{
return inputChannel;
}
public XMLLightweightParser( ReadableByteChannel channel, String charset )
{
rawByteBuffer = new byte[1024];
byteBuffer = ByteBuffer.wrap( rawByteBuffer );
setInput( channel, charset );
}
public XMLLightweightParser( InputStream is , String charset)
{
rawByteBuffer = new byte[1024];
byteBuffer = ByteBuffer.wrap( rawByteBuffer );
setInput( is, charset );
}
public void setInput( InputStream is, String charset )
{
inputChannel = Channels.newChannel( is );
encoder = Charset.forName( charset );
invalidateBuffer();
}
public void setInput( ReadableByteChannel channel, String charset )
{
inputChannel = channel;
encoder = Charset.forName( charset );
invalidateBuffer();
}
/*
* true if the parser has found some complete xml message.
*/
public boolean areThereMsgs()
{
return ( msgs.size() > 0 );
}
/*
* @return an array with all messages found
*/
public String[] getMsgs()
{
String[] res = new String[ msgs.size() ];
for ( int i = 0; i < res.length; i++ )
{
res[ i ] = msgs.get( i );
}
msgs.clear();
invalidateBuffer();
return res;
}
/*
* Method use to re-initialize the buffer
*/
protected void invalidateBuffer()
{
if ( buffer.length() > 0 )
{
String str = buffer.substring( startLastMsg ).toString().trim();
buffer.delete( 0, buffer.length() );
buffer.append( str );
buffer.trimToSize();
}
startLastMsg = 0;
}
/*
* Method that add a message to the list and reinit parser.
*/
protected void foundMsg( String msg )
{
// Add message to the complete message list
if ( msg != null )
{
msgs.add( msg.trim() );
}
// Move the position into the buffer
status = INIT;
tailCount = 0;
cdataOffset = 0;
head.setLength( 0 );
insideRootTag = false;
}
/*
* Main reading method
*/
public void read() throws Exception
{
// Reset buffer
byteBuffer.limit( rawByteBuffer.length );
byteBuffer.rewind();
int readByte = inputChannel.read( byteBuffer );
if ( readByte == -1 )
{
// ERROR ON SOCKET!!
throw new IOException( "ReadByte == -1.Socket Close" );
}
else if ( readByte <= 0 )
{
return;
}
else if ( readByte == 1 && rawByteBuffer[ 0 ] == ' ' )
{
// Heart bit! Ignore it.
return;
}
byteBuffer.flip();
byte[] bhs = byteBuffer.array();
byteBuffer.rewind();
CharBuffer charBuffer = encoder.decode( byteBuffer );
charBuffer.flip();
char[] buf = charBuffer.array();
buffer.append( buf );
// Robot.
char ch;
for ( int i = 0; i < readByte; i++ )
{
//ch = rawByteBuffer[ i ];
ch = buf[ i ];
if ( status == TAIL )
{
// Looking for the close tag
if ( ch == head.charAt( tailCount ) )
{
tailCount++;
if ( tailCount == head.length() )
{
// Close tag found!
// Calculate the correct start,end position of the message into the buffer
int end = buffer.length() - readByte + ( i + 1 );
String msg = buffer.substring( startLastMsg, end );
// Add message to the list
foundMsg( msg );
startLastMsg = end;
}
}
else
{
tailCount = 0;
status = INSIDE;
}
}
else if ( status == PRETAIL )
{
if ( ch == CDATA_START[ cdataOffset ] )
{
cdataOffset++;
if ( cdataOffset == CDATA_START.length )
{
status = INSIDE_CDATA;
cdataOffset = 0;
continue;
}
}
else
{
cdataOffset = 0;
status = INSIDE;
}
if ( ch == '/' )
{
status = TAIL;
}
}
else if ( status == VERIFY_CLOSE_TAG )
{
if ( ch == '>' )
{
// Found a tag in the form <tag />
int end = buffer.length() - readByte + ( i + 1 );
String msg = buffer.substring( startLastMsg, end );
// Add message to the list
foundMsg( msg );
startLastMsg = end;
}
else
{
status = INSIDE;
}
}
else if ( status == INSIDE_PARAM_VALUE )
{
if ( ch == '"' )
{
status = INSIDE;
continue;
}
}
else if ( status == INSIDE_CDATA )
{
if ( ch == CDATA_END[ cdataOffset ] )
{
cdataOffset++;
if ( cdataOffset == CDATA_END.length )
{
status = INSIDE;
cdataOffset = 0;
continue;
}
}
else
{
cdataOffset = 0;
}
}
else if ( status == INSIDE )
{
if ( ch == CDATA_START[ cdataOffset ] )
{
cdataOffset++;
if ( cdataOffset == CDATA_START.length )
{
status = INSIDE_CDATA;
cdataOffset = 0;
continue;
}
}
else
{
cdataOffset = 0;
}
if ( ch == '"' )
{
status = INSIDE_PARAM_VALUE;
}
else if ( ch == '>' )
{
if ( insideRootTag && "stream:stream>".equals( head.toString() ) )
{
// Found closing stream:stream
int end = buffer.length() - readByte + ( i + 1 );
String msg = buffer.substring( startLastMsg, end );
foundMsg( msg );
startLastMsg = end;
}
insideRootTag = false;
}
else if ( ch == '<' )
{
status = PRETAIL;
}
else if ( ch == '/' && insideRootTag )
{
status = VERIFY_CLOSE_TAG;
}
}
else if ( status == HEAD )
{
if ( ch == ' ' || ch == '>' )
{
// Append > to head to facility the research of </tag>
head.append( ">" );
status = INSIDE;
insideRootTag = true;
continue;
}
head.append( (char)ch );
}
else if ( status == INIT )
{
if ( ch != ' ' && ch != '\r' && ch != '\n' && ch != '<' )
{
invalidateBuffer();
return;
}
if ( ch == '<' )
{
status = HEAD;
}
}
}
}
}
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