All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cometd.server.BayeuxServerImpl Maven / Gradle / Ivy

package org.cometd.server;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.cometd.bayeux.Channel;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.Transport;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.LocalSession;
import org.cometd.bayeux.server.SecurityPolicy;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.bayeux.server.ServerMessage.Mutable;
import org.cometd.common.ChannelId;
import org.eclipse.jetty.util.ajax.JSON;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Timeout;


/* ------------------------------------------------------------ */
/**
 * 
 * Options to configure the server are: 
* tickIntervalMsThe time in milliseconds between ticks to check for timeouts etc * sweepIntervalMsThe time in milliseconds between sweeps of channels to remove * invalid subscribers and non-persistent channels *
*/ public class BayeuxServerImpl extends AbstractLifeCycle implements BayeuxServer { private final Logger _logger; private final SecureRandom _random = new SecureRandom(); private final List _listeners = new CopyOnWriteArrayList(); private final List _extensions = new CopyOnWriteArrayList(); private final ServerMessagePool _pool = new ServerMessagePool(); private final ServerChannelImpl _root=new ServerChannelImpl(this,null,new ChannelId("/")); private final ConcurrentMap _sessions = new ConcurrentHashMap(); private final ConcurrentMap _channels = new ConcurrentHashMap(); private final ConcurrentMap _transports = new ConcurrentHashMap(); private final List _allowedTransports = new CopyOnWriteArrayList(); private final ThreadLocal _currentTransport = new ThreadLocal(); private final Map _options = new TreeMap(); private final Timeout _timeout = new Timeout(); private SecurityPolicy _policy=new DefaultSecurityPolicy(); private Timer _timer = new Timer(); private Object _handshakeAdvice=new JSON.Literal("{\"reconnect\":\"handshake\",\"interval\":500}"); /* ------------------------------------------------------------ */ protected BayeuxServerImpl() { getChannel(Channel.META_HANDSHAKE,true).addListener(new HandshakeHandler()); getChannel(Channel.META_CONNECT,true).addListener(new ConnectHandler()); getChannel(Channel.META_SUBSCRIBE,true).addListener(new SubscribeHandler()); getChannel(Channel.META_UNSUBSCRIBE,true).addListener(new UnsubscribeHandler()); getChannel(Channel.META_DISCONNECT,true).addListener(new DisconnectHandler()); _logger=Log.getLogger("bayeux@"+hashCode()); _logger.info("STARTED: "+_sessions); setOption("tickIntervalMs","97"); setOption("sweepIntervalMs","9997"); } /* ------------------------------------------------------------ */ public Logger getLogger() { return _logger; } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() */ @Override protected void doStart() throws Exception { super.doStart(); _timer=new Timer("BayeuxServer@" +hashCode(),true); long tick_interval = getLongOptions("tickIntervalMs",-1); if (tick_interval>0) _timer.schedule(new TimerTask() { @Override public void run() { _timeout.tick(System.currentTimeMillis()); } },tick_interval,tick_interval); long sweep_interval = getLongOptions("sweepIntervalMs",-1); if (sweep_interval>0) _timer.schedule(new TimerTask() { @Override public void run() { _root.doSweep(); } },sweep_interval,sweep_interval); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() */ @Override protected void doStop() throws Exception { super.doStop(); _timer.cancel(); _timer=null; } /* ------------------------------------------------------------ */ public void startTimeout(Timeout.Task task, long interval) { _timeout.schedule(task,interval); } /* ------------------------------------------------------------ */ public void cancelTimeout(Timeout.Task task) { task.cancel(); } /* ------------------------------------------------------------ */ public ChannelId newChannelId(String id) { ServerChannelImpl channel = _channels.get(id); if (channel!=null) return channel.getChannelId(); return new ChannelId(id); } /* ------------------------------------------------------------ */ public Map getOptions() { return _options; } /* ------------------------------------------------------------ */ /** * @see org.cometd.bayeux.Bayeux#getOption(java.lang.String) */ public Object getOption(String qualifiedName) { return _options.get(qualifiedName); } /* ------------------------------------------------------------ */ /** Get an option value as a long * @param name * @param dft The default value * @return long value */ protected long getLongOptions(String name,long dft) { Object val=getOption(name); if (val instanceof Long) return ((Long)val).longValue(); if (val!=null) return Long.valueOf(val.toString()); return dft; } /* ------------------------------------------------------------ */ /** * @see org.cometd.bayeux.Bayeux#getOptionNames() */ public Set getOptionNames() { return _options.keySet(); } /* ------------------------------------------------------------ */ /** * @see org.cometd.bayeux.Bayeux#setOption(java.lang.String, java.lang.Object) */ public void setOption(String qualifiedName, Object value) { _options.put(qualifiedName,value); } /* ------------------------------------------------------------ */ public int randomInt() { return _random.nextInt(); } /* ------------------------------------------------------------ */ public int randomInt(int n) { return _random.nextInt(n); } /* ------------------------------------------------------------ */ public long randomLong() { return _random.nextLong(); } /* ------------------------------------------------------------ */ public ServerChannelImpl root() { return _root; } /* ------------------------------------------------------------ */ public ServerMessagePool getServerMessagePool() { return _pool; } /* ------------------------------------------------------------ */ public void setCurrentTransport(ServerTransport transport) { _currentTransport.set(transport); } /* ------------------------------------------------------------ */ public ServerTransport getCurrentTransport() { return _currentTransport.get(); } /* ------------------------------------------------------------ */ public SecurityPolicy getSecurityPolicy() { return _policy; } /* ------------------------------------------------------------ */ public ServerChannel getChannel(String channelId, boolean create) { ServerChannelImpl channel = _channels.get(channelId); if (channel==null && create) channel=_root.getChild(new ChannelId(channelId),true); return channel; } /* ------------------------------------------------------------ */ public Collection getSessions() { return Collections.unmodifiableCollection(_sessions.values()); } /* ------------------------------------------------------------ */ public ServerSession getSession(String clientId) { if (clientId==null) return null; return _sessions.get(clientId); } /* ------------------------------------------------------------ */ protected void addServerSession(ServerSessionImpl session) { _sessions.put(session.getId(),session); for (BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.SessionListener) ((SessionListener)listener).sessionAdded(session); } } /* ------------------------------------------------------------ */ /** * @param session * @param timedout * @return true if the session was removed and was connected */ public boolean removeServerSession(ServerSession session,boolean timedout) { if (_logger.isDebugEnabled()) _logger.debug("remove "+session+(timedout?" timedout":"")); ServerSessionImpl removed =_sessions.remove(session.getId()); if(removed==session) { for (BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.SessionListener) ((SessionListener)listener).sessionRemoved(session,timedout); } return ((ServerSessionImpl)session).removed(timedout); } else return false; } /* ------------------------------------------------------------ */ protected ServerSessionImpl newServerSession() { return new ServerSessionImpl(this); } /* ------------------------------------------------------------ */ protected ServerSessionImpl newServerSession(LocalSessionImpl local, String idHint) { return new ServerSessionImpl(this,local,idHint); } /* ------------------------------------------------------------ */ public LocalSession newLocalSession(String idHint) { return new LocalSessionImpl(this,idHint); } /* ------------------------------------------------------------ */ public ServerMessage.Mutable newMessage() { return _pool.getServerMessage(); } /* ------------------------------------------------------------ */ public ServerMessage.Mutable newMessage(ServerMessage tocopy) { ServerMessage.Mutable mutable = _pool.getServerMessage(); for (String key : tocopy.keySet()) mutable.put(key,tocopy.get(key)); return mutable; } /* ------------------------------------------------------------ */ public void setSecurityPolicy(SecurityPolicy securityPolicy) { _policy=securityPolicy; } /* ------------------------------------------------------------ */ public void addExtension(Extension extension) { _extensions.add(extension); } /* ------------------------------------------------------------ */ public void addListener(BayeuxServerListener listener) { if (!(listener instanceof BayeuxServerListener)) throw new IllegalArgumentException("!BayeuxServerListener"); _listeners.add((BayeuxServerListener)listener); } /* ------------------------------------------------------------ */ public ServerChannel getChannel(String channelId) { return _channels.get(channelId); } /* ------------------------------------------------------------ */ public void removeListener(BayeuxServerListener listener) { _listeners.remove(listener); } /* ------------------------------------------------------------ */ /** Extend and handle in incoming message. * @param session The session if known * @param message The message. * @return An unextended reply message */ public ServerMessage handle(ServerSessionImpl session, ServerMessage.Mutable message) { ServerMessage.Mutable reply=null; if (_logger.isDebugEnabled()) _logger.debug("> "+message+" "+session); if (!extendRecv(session,message) || session!=null && !session.extendRecv(message)) { reply=createReply(message); reply.setSuccessful(false); reply.put(Message.ERROR_FIELD,"404::Message deleted"); } else { if (_logger.isDebugEnabled()) _logger.debug(">> "+message); String channelId=message.getChannel(); ServerChannel channel=null; if (channelId!=null) { channel = getChannel(channelId,false); if (channel==null && _policy.canCreate(this,session,channelId,message)) channel = getChannel(channelId,true); } if (channel==null) { reply = createReply(message); error(reply,channelId==null?"402::no channel":"403::Cannot create"); } else if (channel.isMeta()) { root().doPublish(session,(ServerChannelImpl)channel,message); reply = message.getAssociated().asMutable(); } else if (_policy.canPublish(this,session,channel,message)) { // This is a normal publish, // if this is not a local client, lets create a new message as we // don't trust what else was in their message. if (session!=null && session.isLocalSession() || channel.isService()) { message.setClientId(null); channel.publish(session,message); } else { ServerMessage.Mutable out = newMessage(); out.setChannel(message.getChannel()); out.setData(message.getData()); out.setId(message.getId()); out.incRef(); channel.publish(session,out); out.decRef(); } reply = createReply(message); reply.setSuccessful(true); } else { reply = createReply(message); error(reply,session==null?"402::unknown client":"403::Cannot publish"); } } if (_logger.isDebugEnabled()) _logger.debug("<< "+reply); return reply; } /* ------------------------------------------------------------ */ public ServerMessage extendReply(ServerSessionImpl session, ServerMessage reply) { if (session!=null) reply = session.extendSend(reply); if (reply!=null && !extendSend(session,reply.asMutable())) reply=null; if (_logger.isDebugEnabled()) _logger.debug("< "+reply); return reply; } /* ------------------------------------------------------------ */ protected boolean extendRecv(ServerSessionImpl from, ServerMessage.Mutable message) { if (message.isMeta()) { for (Extension ext: _extensions) if (!ext.rcvMeta(from,message)) return false; } else { for (Extension ext: _extensions) if (!ext.rcv(from,message)) return false; } return true; } /* ------------------------------------------------------------ */ protected boolean extendSend(ServerSessionImpl to, ServerMessage.Mutable message) { if (message.isMeta()) { ListIterator i = _extensions.listIterator(_extensions.size()); while(i.hasPrevious()) if (!i.previous().sendMeta(to,message)) return false; } else { ListIterator i = _extensions.listIterator(_extensions.size()); while(i.hasPrevious()) if (!i.previous().send(message)) return false; } return true; } /* ------------------------------------------------------------ */ void addServerChannel(ServerChannelImpl channel) { ServerChannelImpl old = _channels.putIfAbsent(channel.getId(),channel); if (old!=null) throw new IllegalStateException(); for (BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.ChannelListener) ((ChannelListener)listener).channelAdded(channel); } } /* ------------------------------------------------------------ */ boolean removeServerChannel(ServerChannelImpl channel) { if(_channels.remove(channel.getId(),channel)) { for (BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.ChannelListener) ((ChannelListener)listener).channelRemoved(channel.getId()); } return true; } return false; } /* ------------------------------------------------------------ */ List getListeners() { return _listeners; } /* ------------------------------------------------------------ */ public List getAllowedTransports() { return Collections.unmodifiableList(_allowedTransports); } /* ------------------------------------------------------------ */ public Set getKnownTransportNames() { return _transports.keySet(); } /* ------------------------------------------------------------ */ public Transport getTransport(String transport) { return _transports.get(transport); } /* ------------------------------------------------------------ */ public void addTransport(Transport transport) { _transports.put(transport.getName(),transport); } /* ------------------------------------------------------------ */ public void setAllowedTransports(String... allowed) { setAllowedTransports(Arrays.asList(allowed)); } /* ------------------------------------------------------------ */ public void setAllowedTransports(List allowed) { _allowedTransports.clear(); for (String transport : allowed) { if (_transports.containsKey(transport)) _allowedTransports.add(transport); } } /* ------------------------------------------------------------ */ protected void error(ServerMessage.Mutable reply, String error) { reply.put(Message.ERROR_FIELD,error); reply.setSuccessful(false); } /* ------------------------------------------------------------ */ protected ServerMessage.Mutable createReply(ServerMessage.Mutable message) { ServerMessage.Mutable reply=newMessage(); message.setAssociated(reply); reply.setAssociated(message); reply.setChannel(message.getChannel()); Object id=message.getId(); if (id != null) reply.setId(id); return reply; } /* ------------------------------------------------------------ */ protected ServerChannelImpl getRootChannel() { return _root; } /* ------------------------------------------------------------ */ public String dump() { StringBuilder b = new StringBuilder(); _root.dump(b,""); return b.toString(); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ abstract class HandlerListener implements ServerChannel.ServerChannelListener { public abstract void onMessage(final ServerSessionImpl from, final ServerMessage.Mutable message); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class HandshakeHandler extends HandlerListener { @Override public void onMessage(ServerSessionImpl session, final Mutable message) { if (session==null) session = newServerSession(); ServerMessage.Mutable reply=createReply(message); if (_policy != null && !_policy.canHandshake(BayeuxServerImpl.this,session,message)) { error(reply,"403::Handshake denied"); return; } session.handshake(); addServerSession(session); reply.setSuccessful(true); reply.put(Message.CLIENT_FIELD,session.getId()); reply.put(Message.VERSION_FIELD,"1.0"); reply.put(Message.MIN_VERSION_FIELD,"1.0"); reply.put(Message.SUPPORTED_CONNECTION_TYPES_FIELD,getAllowedTransports()); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class ConnectHandler extends HandlerListener { @Override public void onMessage(final ServerSessionImpl session, final Mutable message) { ServerMessage.Mutable reply=createReply(message); if (session == null) { error(reply,"402::Unknown client"); reply.put(Message.ADVICE_FIELD,_handshakeAdvice); return; } session.connect(_timeout.getNow()); // receive advice Map adviceIn=message.getAdvice(); if (adviceIn != null) { Long timeout=(Long)adviceIn.get("timeout"); session.setTimeout(timeout==null?0:timeout.longValue()); Long interval=(Long)adviceIn.get("interval"); session.setInterval(interval==null?0:interval.longValue()); } // send advice Object adviceOut = session.takeAdvice(); if (adviceOut!=null) reply.put(Message.ADVICE_FIELD,adviceOut); reply.setSuccessful(true); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class SubscribeHandler extends HandlerListener { public void onMessage(final ServerSessionImpl from, final Mutable message) { ServerMessage.Mutable reply=createReply(message); if (from == null) { error(reply,"402::Unknown client"); reply.put(Message.ADVICE_FIELD,_handshakeAdvice); return; } String subscribe_id=(String)message.get(Message.SUBSCRIPTION_FIELD); reply.put(Message.SUBSCRIPTION_FIELD,subscribe_id); if (subscribe_id==null) error(reply,"403::cannot create"); else { reply.put(Message.SUBSCRIPTION_FIELD,subscribe_id); ServerChannelImpl channel = (ServerChannelImpl)getChannel(subscribe_id); if (channel==null && getSecurityPolicy().canCreate(BayeuxServerImpl.this,from,subscribe_id,message)) channel = (ServerChannelImpl)getChannel(subscribe_id,true); if (channel==null) error(reply,"403::cannot create"); else if (!getSecurityPolicy().canSubscribe(BayeuxServerImpl.this,from,channel,message)) error(reply,"403::cannot subscribe"); else { if (from.isLocalSession() || !channel.isMeta() && !channel.isService()) { if (channel.subscribe((ServerSessionImpl)from)) reply.setSuccessful(true); else error(reply,"403::subscribe failed"); } else reply.setSuccessful(true); } } } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class UnsubscribeHandler extends HandlerListener { public void onMessage(final ServerSessionImpl from, final Mutable message) { ServerMessage.Mutable reply=createReply(message); if (from == null) { error(reply,"402::Unknown client"); reply.put(Message.ADVICE_FIELD,_handshakeAdvice); return; } String subscribe_id=(String)message.get(Message.SUBSCRIPTION_FIELD); reply.put(Message.SUBSCRIPTION_FIELD,subscribe_id); if (subscribe_id==null) error(reply,"400::no channel"); else { reply.put(Message.SUBSCRIPTION_FIELD,subscribe_id); ServerChannelImpl channel = (ServerChannelImpl)getChannel(subscribe_id); if (channel==null) error(reply,"400::no channel"); else { if (from.isLocalSession() || !channel.isMeta() && !channel.isService()) channel.unsubscribe((ServerSessionImpl)from); reply.setSuccessful(true); } } } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class DisconnectHandler extends HandlerListener { public void onMessage(final ServerSessionImpl session, final Mutable message) { ServerMessage.Mutable reply=createReply(message); if (session == null) { error(reply,"402::Unknown client"); return; } removeServerSession(session,false); session.dispatch(); reply.setSuccessful(true); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy