/* * Copyright 2009 Red Hat, Inc. * * Red Hat licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.jboss.netty.handler.codec.bayeux; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * BayeuxRouter is another core part of Bayeux. It's a singleton class holding * all connections with routing table and their subscriptions. * * @author daijun */ public class BayeuxRouter { private final Map<String, BayeuxConnection> connections = new HashMap<String, BayeuxConnection>(); private final Map<String, List<BayeuxConnection>> subscriptions = new HashMap<String, List<BayeuxConnection>>(); private static final BayeuxRouter instance = new BayeuxRouter(); private BayeuxRouter() { } /** * Return the instance of BayeuxRouter. * * @return */ public static synchronized BayeuxRouter getInstance() { return instance; } /** * Get a connection instance by client id. If router dose't exist a * connection by the cilent id, return null. * * @param clientId * @return */ public BayeuxConnection getConnection(String clientId) { return connections.get(clientId); } /** * Clear router's connections and subscriptions. */ public void clear() { subscriptions.clear(); for (Entry<String, BayeuxConnection> entry : connections.entrySet()) { entry.getValue().close(); } connections.clear(); } /** * Add a connection to router. * * @param connection */ public void addConnection(BayeuxConnection connection) { String clientId = null; do { clientId = BayeuxUtil.generateUUID(); } while (connections.containsKey(clientId)); connection.setClientId(clientId); connections.put(clientId, connection); } /** * Remove a connection from router as well as it's subscriptions. * * @param connection * @return */ public boolean removeConnection(BayeuxConnection connection) { if (getConnection(connection.getClientId()) != null) { for (String subscription : connection.getSubscriptions()) { removeListener(subscription, connection); } connections.remove(connection.getClientId()); return true; } else { return false; } } /** * Add a new subscription to a connection. Return false if the connection * already subscribes it, and return true if not. * * @param subscription * @param connection * @return */ public boolean addListener(String subscription, BayeuxConnection connection) { if (subscription.endsWith("/")) { subscription = subscription.substring(0, (subscription.length() - 1)); } List<BayeuxConnection> listeners = subscriptions.get(subscription); if (listeners == null) { listeners = new ArrayList<BayeuxConnection>(); subscriptions.put(subscription, listeners); } if (listeners.contains(connection)) { return false; } listeners.add(connection); return true; } /** * Remove a subscription from a connection and return true. * * @param subscription * @param connection * @return */ public boolean removeListener(String subscription, BayeuxConnection connection) { for (String sub : BayeuxUtil.prefixMatch(subscription, subscriptions.keySet())) { List<BayeuxConnection> listeners = subscriptions.get(sub); if (listeners.contains(connection)) { listeners.remove(connection); if (listeners.isEmpty()) { subscriptions.remove(subscription); } } } return true; } /** * Returns current number of connections. * * @return */ public int countConnections() { return connections.size(); } /** * Process PublishRequest and send its data to subscribing clients by router. * * @deprecated use publish(BayeuxConnection publisher, DeliverEvent deliver) instead * @param publishRequest * @return */ public boolean onPublish(PublishRequest publishRequest) { String subscription = publishRequest.getChannel(); if (subscription == null || subscription.length() == 0) { return false; } List<BayeuxConnection> matchedConnections = new ArrayList<BayeuxConnection>(); for (String sub : BayeuxUtil.prefixMatch(subscription, subscriptions.keySet())) { for (BayeuxConnection conn : subscriptions.get(sub)) { if (!matchedConnections.contains(conn)) { matchedConnections.add(conn); } } } for (BayeuxConnection connection : matchedConnections) { DeliverEvent deliverEvent = new DeliverEvent(publishRequest); deliverEvent.setId(connection.getId()); if (connection.getClientId().equals(publishRequest.getClientId())) { connection.setId(publishRequest.getId()); connection.putToDownstream(deliverEvent); } else { connection.send(deliverEvent); } } return true; } /** * Process PublishRequest and send its data to subscribing clients by router in batch. It will return false, if publishing fails for once. * * @deprecated use publish(BayeuxConnection publisher, List<DeliverEvent> deliverList) instead * @param list * @return */ public boolean onPublish(List<PublishRequest> list) { boolean result = true; for (PublishRequest publish : list) { if (!onPublish(publish)) { result = false; } } return result; } /** * Publish data to subscribing clients. * * @param publisher * @param deliver * @return */ public boolean publish(BayeuxConnection publisher, DeliverEvent deliver) { if (!deliver.isValid()) { return false; } String subscription = deliver.getChannel(); if (subscription == null || subscription.length() == 0) { return false; } List<BayeuxConnection> matchedConnections = new ArrayList<BayeuxConnection>(); for (String sub : BayeuxUtil.prefixMatch(subscription, subscriptions.keySet())) { for (BayeuxConnection conn : subscriptions.get(sub)) { if (!matchedConnections.contains(conn)) { matchedConnections.add(conn); } } } for (BayeuxConnection connection : matchedConnections) { if (connection == publisher) { connection.putToDownstream(deliver); } else { connection.send(deliver); } } return true; } /** * Publish data to subscribing clients in batch. It will return false, if publishing fails onece. * * @param publisher * @param deliverList * @return */ public boolean publish(BayeuxConnection publisher, List<DeliverEvent> deliverList) { boolean result = true; for (DeliverEvent deliver : deliverList) { if (!publish(publisher, deliver)) { result = false; } } return result; } /** * Returns all the connections in router. * * @return */ public Map<String, BayeuxConnection> getConnections() { return connections; } /** * Returns all the subscription relationships. * * @return */ public Map<String, List<BayeuxConnection>> getSubscriptions() { return subscriptions; } }