// ---------------------------------------------------------------------------
// jWebSocket - Basic server (dispatcher)
// Copyright (c) 2010 Alexander Schulze, Innotrade GmbH
// ---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
// more details.
// You should have received a copy of the GNU Lesser General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
// ---------------------------------------------------------------------------
package org.jwebsocket.server;
import java.util.List;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import org.jwebsocket.api.ServerConfiguration;
import org.jwebsocket.api.WebSocketConnector;
import org.jwebsocket.api.WebSocketEngine;
import org.jwebsocket.api.WebSocketPacket;
import org.jwebsocket.api.WebSocketServer;
import org.jwebsocket.kit.BroadcastOptions;
import org.jwebsocket.kit.CloseReason;
import org.jwebsocket.api.WebSocketPlugInChain;
import org.jwebsocket.api.WebSocketFilterChain;
import org.jwebsocket.api.WebSocketServerListener;
import org.jwebsocket.connectors.BaseConnector;
import org.jwebsocket.kit.WebSocketServerEvent;
import org.jwebsocket.kit.WebSocketException;
/**
* The implementation of the basic websocket server. A server is the central
* instance which either processes incoming data from the engines directly or
* routes it to the chain of plug-ins. Each server maintains a FastMap of underlying
* engines. An application can instantiate multiple servers to process different
* kinds of data packets.
* @author aschulze
*/
public class BaseServer implements WebSocketServer {
private Map<String, WebSocketEngine> mEngines = null;
private String mId = null;
protected WebSocketPlugInChain plugInChain = null;
protected WebSocketFilterChain filterChain = null;
private List<WebSocketServerListener> mListeners = new FastList<WebSocketServerListener>();
/**
* Create a new instance of the Base Server. Each BaseServer maintains a
* FastMap of all its underlying engines. Each Server has an Id whioch can be
* used to easily address a certain server.
* @param aId Id for the new server.
*/
public BaseServer(ServerConfiguration aServerConfig) {
mId = aServerConfig.getId();
mEngines = new FastMap<String, WebSocketEngine>();
}
@Override
/**
* {@inheritDoc }
*/
public void addEngine(WebSocketEngine aEngine) {
mEngines.put(aEngine.getId(), aEngine);
aEngine.addServer(this);
}
@Override
/**
* {@inheritDoc }
*/
public void removeEngine(WebSocketEngine aEngine) {
mEngines.remove(aEngine.getId());
aEngine.removeServer(this);
}
/**
* {@inheritDoc }
*/
@Override
public void startServer()
throws WebSocketException {
// this method is supposed to be overwritten by descending classes.
}
/**
* {@inheritDoc }
*/
@Override
public boolean isAlive() {
// this method is supposed to be overwritten by descending classes.
return false;
}
/**
* {@inheritDoc }
*/
@Override
public void stopServer()
throws WebSocketException {
// this method is supposed to be overwritten by descending classes.
}
/**
* {@inheritDoc }
*/
@Override
public void engineStarted(WebSocketEngine aEngine) {
// this method is supposed to be overwritten by descending classes.
// e.g. to notify the overlying appplications or plug-ins
// about the engineStarted event
}
/**
* {@inheritDoc }
*/
@Override
public void engineStopped(WebSocketEngine aEngine) {
// this method is supposed to be overwritten by descending classes.
// e.g. to notify the overlying appplications or plug-ins
// about the engineStopped event
}
/**
* {@inheritDoc }
*/
@Override
public void connectorStarted(WebSocketConnector aConnector) {
// this method is supposed to be overwritten by descending classes.
// e.g. to notify the overlying appplications or plug-ins
// about the connectorStarted event
WebSocketServerEvent lEvent = new WebSocketServerEvent(aConnector, this);
for (WebSocketServerListener lListener : mListeners) {
if (lListener != null) {
lListener.processOpened(lEvent);
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void connectorStopped(WebSocketConnector aConnector, CloseReason aCloseReason) {
// this method is supposed to be overwritten by descending classes.
// e.g. to notify the overlying appplications or plug-ins
// about the connectorStopped event
WebSocketServerEvent lEvent = new WebSocketServerEvent(aConnector, this);
for (WebSocketServerListener lListener : mListeners) {
if (lListener != null) {
lListener.processClosed(lEvent);
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void processPacket(WebSocketEngine aEngine, WebSocketConnector aConnector, WebSocketPacket aDataPacket) {
// this method is supposed to be overwritten by descending classes.
WebSocketServerEvent lEvent = new WebSocketServerEvent(aConnector, this);
for (WebSocketServerListener lListener : getListeners()) {
if (lListener != null) {
lListener.processPacket(lEvent, aDataPacket);
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void sendPacket(WebSocketConnector aConnector, WebSocketPacket aDataPacket) {
// send a data packet to the passed connector
aConnector.sendPacket(aDataPacket);
}
/**
* {@inheritDoc }
*/
@Override
public void broadcastPacket(WebSocketConnector aSource, WebSocketPacket aDataPacket,
BroadcastOptions aBroadcastOptions) {
for (WebSocketConnector lConnector : getAllConnectors().values()) {
if (!aSource.equals(lConnector) || aBroadcastOptions.isSenderIncluded()) {
sendPacket(lConnector, aDataPacket);
}
}
}
/**
* returns the FastMap of all underlying engines. Each engine has its own unique
* id which is used as key in the FastMap.
* @return FastMap with the underlying engines.
*/
public Map<String, WebSocketEngine> getEngines() {
// return (engines != null ? (FastMap)(engines.unmodifiable()) : null);
return (mEngines != null ? mEngines : null);
}
/**
* returns all connectors of the passed engine as a FastMap. Each connector has
* its own unique id which is used as key in the connectors FastMap.
* @param aEngine
* @return the engines
*/
@Override
public Map<String, WebSocketConnector> getConnectors(WebSocketEngine aEngine) {
// TODO: does this need to be so nested?
// return (FastMap)((FastMap)aEngine.getConnectors()).unmodifiable();
return aEngine.getConnectors();
}
/**
* returns all connectors of all engines connected to the server. Each
* connector has its own unique id which is used as key in the connectors
* FastMap.
* @return the engines
*/
@Override
public Map<String, WebSocketConnector> getAllConnectors() {
Map<String, WebSocketConnector> lClients = new FastMap<String, WebSocketConnector>();
for (WebSocketEngine lEngine : mEngines.values()) {
lClients.putAll(lEngine.getConnectors());
}
return lClients;
}
/**
* returns only those connectors that match the passed shared variables.
* The search criteria is passed as a FastMap with key/value pairs. The key
* represents the name of the shared custom variable for the connector and
* the value the value for that variable. If multiple key/value pairs are
* passed they are combined by a logical 'and'.
* Each connector has its own unique id which is used as key in the
* connectors FastMap.
* @param aFilter FastMap of key/values pairs as search criteria.
* @return FastMap with the selected connector or empty FastMap if no connector matches the search criteria.
*/
@Override
public Map<String, WebSocketConnector> selectConnectors(Map<String, Object> aFilter) {
Map<String, WebSocketConnector> lClients = new FastMap<String, WebSocketConnector>();
for (WebSocketEngine lEngine : mEngines.values()) {
for (WebSocketConnector lConnector : lEngine.getConnectors().values()) {
boolean lMatch = true;
for (String lKey : aFilter.keySet()) {
Object lVarVal = lConnector.getVar(lKey);
lMatch = (lVarVal != null);
if (lMatch) {
Object lFilterVal = aFilter.get(lKey);
if (lVarVal instanceof String && lFilterVal instanceof String) {
lMatch = ((String) lVarVal).matches((String) lFilterVal);
} else if (lVarVal instanceof Boolean) {
lMatch = ((Boolean) lVarVal).equals((Boolean) lFilterVal);
} else {
lMatch = lVarVal.equals(lFilterVal);
}
if (!lMatch) {
break;
}
}
}
if (lMatch) {
lClients.put(lConnector.getId(), lConnector);
}
}
}
// return (FastMap)(lClients.unmodifiable());
return lClients;
}
/**
* Returns the connector identified by it's connector-id or <tt>null</tt>
* if no connector with that id could be found. This method iterates
* through all embedded engines.
* @param aId id of the connector to be returned.
* @return WebSocketConnector with the given id or <tt>null</tt> if not found.
*/
@Override
public WebSocketConnector getConnector(String aId) {
for (WebSocketEngine lEngine : mEngines.values()) {
WebSocketConnector lConnector = lEngine.getConnectors().get(aId);
if (lConnector != null) {
return lConnector;
}
}
return null;
}
/**
* Returns the connector identified by it's node-id or <tt>null</tt>
* if no connector with that id could be found. This method iterates
* through all embedded engines.
* @param aId id of the connector to be returned.
* @return WebSocketConnector with the given id or <tt>null</tt> if not found.
*/
@Override
public WebSocketConnector getNode(String aNodeId) {
if (aNodeId != null) {
for (WebSocketEngine lEngine : mEngines.values()) {
for (WebSocketConnector lConnector : lEngine.getConnectors().values()) {
if (aNodeId.equals(lConnector.getString(BaseConnector.VAR_NODEID))) {
return lConnector;
}
}
}
}
return null;
}
/**
* Returns the connector identified by it's connector-id or <tt>null</tt> if
* no connector with that id could be found. Only the connectors of the
* engine identified by the passed engine are considered. If not engine
* with that id could be found <tt>null</tt> is returned.
* @param aEngine id of the engine of the connector.
* @param aId id of the connector to be returned
* @return WebSocketConnector with the given id or <tt>null</tt> if not found.
*/
public WebSocketConnector getConnector(String aEngine, String aId) {
WebSocketEngine lEngine = mEngines.get(aEngine);
if (lEngine != null) {
return lEngine.getConnectors().get(aId);
}
return null;
}
/**
* Returns the connector identified by it's connector-id or <tt>null</tt> if
* no connector with that id could be found. Only the connectors of the
* passed engine are considered. If no engine is passed <tt>null</tt> is
* returned.
* @param aEngine reference to the engine of the connector.
* @param aId id of the connector to be returned
* @return WebSocketConnector with the given id or <tt>null</tt> if not found.
*/
public WebSocketConnector getConnector(WebSocketEngine aEngine, String aId) {
if (aEngine != null) {
return aEngine.getConnectors().get(aId);
}
return null;
}
/**
* Returns the unique id of the server. Once set by the constructor the id
* cannot be changed anymore by the application.
* @return Id of this server instance.
*/
@Override
public String getId() {
return mId;
}
@Override
public WebSocketPlugInChain getPlugInChain() {
return plugInChain;
}
@Override
public WebSocketFilterChain getFilterChain() {
return filterChain;
}
@Override
public void addListener(WebSocketServerListener aListener) {
mListeners.add(aListener);
}
@Override
public void removeListener(WebSocketServerListener aListener) {
mListeners.remove(aListener);
}
/**
* @return the listeners
*/
@Override
public List<WebSocketServerListener> getListeners() {
return mListeners;
}
/**
*
* @param aConnector
* @return
*/
@Override
public String getUsername(WebSocketConnector aConnector) {
return aConnector.getString(BaseConnector.VAR_USERNAME);
}
/**
*
* @param aConnector
* @param aUsername
*/
@Override
public void setUsername(WebSocketConnector aConnector, String aUsername) {
aConnector.setString(BaseConnector.VAR_USERNAME, aUsername);
}
/**
*
* @param aConnector
*/
@Override
public void removeUsername(WebSocketConnector aConnector) {
aConnector.removeVar(BaseConnector.VAR_USERNAME);
}
/**
*
* @param aConnector
* @return
*/
@Override
public String getNodeId(WebSocketConnector aConnector) {
return aConnector.getString(BaseConnector.VAR_NODEID);
}
/**
*
* @param aConnector
* @param aNodeId
*/
@Override
public void setNodeId(WebSocketConnector aConnector, String aNodeId) {
aConnector.setString(BaseConnector.VAR_NODEID, aNodeId);
}
/**
*
* @param aConnector
*/
@Override
public void removeNodeId(WebSocketConnector aConnector) {
aConnector.removeVar(BaseConnector.VAR_NODEID);
}
}