/* * Copyright 2017 Async-IO.org * * Licensed 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.atmosphere.plugin.redis.redisson; import org.atmosphere.cpr.AtmosphereConfig; import org.redisson.Config; import org.redisson.Redisson; import org.redisson.client.protocol.pubsub.Message; import org.redisson.connection.RandomLoadBalancer; import org.redisson.core.MessageListener; import org.redisson.core.PatternMessageListener; import org.redisson.core.RPatternTopic; import org.redisson.core.RTopic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.util.Arrays; import java.util.List; public class RedissonUtil { private static final Logger logger = LoggerFactory.getLogger(RedissonBroadcaster.class); private static final String REDIS_AUTH = RedissonBroadcaster.class.getName() + ".authorization"; private static final String REDIS_SERVER = RedissonBroadcaster.class.getName() + ".server"; private static final String REDIS_OTHERS = RedissonBroadcaster.class.getName() + ".others"; private static final String REDIS_TYPE = RedissonBroadcaster.class.getName() + ".type"; private static final String REDIS_SCAN_INTERVAL = RedissonBroadcaster.class.getName() + ".scan.interval"; private static final String REDIS_SENTINEL_MASTER_NAME = RedissonBroadcaster.class.getName() + ".master.name"; private Redisson redisson; private final AtmosphereConfig config; private URI uri; private final Callback callback; public RedissonUtil(URI uri, AtmosphereConfig config, Callback callback) { this.config = config; this.callback = callback; this.uri = uri; } private enum RedisType { SINGLE("single"), MASTER("master"), CLUSTER("cluster"), SENTINEL("sentinel"), ELASTICACHE("elasticache"); private String stringValue; RedisType(String s) { stringValue = s; } public String getStringValue() { return stringValue; } } public void configure() { String authToken = ""; String redisType = ""; if (config.getServletConfig().getInitParameter(REDIS_TYPE) != null) { redisType = config.getServletConfig().getInitParameter(REDIS_TYPE); } if (config.getServletConfig().getInitParameter(REDIS_AUTH) != null) { authToken = config.getServletConfig().getInitParameter(REDIS_AUTH); } if (config.getServletConfig().getInitParameter(REDIS_SERVER) != null) { uri = URI.create(config.getServletConfig().getInitParameter(REDIS_SERVER)); } else if (uri == null) { throw new NullPointerException("uri cannot be null"); } Config redissonConfig = new Config(); if (redisType.isEmpty() || redisType.equals(RedisType.SINGLE.getStringValue())) { redissonConfig.useSingleServer().setAddress(uri.getHost() + ":" + uri.getPort()); redissonConfig.useSingleServer().setDatabase(1); if (!authToken.isEmpty()) { redissonConfig.useSingleServer().setPassword(authToken); } } else { List<String> slaveList = Arrays.asList(config.getServletConfig().getInitParameter(REDIS_OTHERS).split("\\s*,\\s*")); Integer scanInterval = 2000; if (config.getServletConfig().getInitParameter(REDIS_SCAN_INTERVAL) != null) { scanInterval = Integer.parseInt(config.getServletConfig().getInitParameter(REDIS_SCAN_INTERVAL)); } if (redisType.equals(RedisType.MASTER.getStringValue())) { redissonConfig.useMasterSlaveConnection() .setMasterAddress(uri.getHost() + ":" + uri.getPort()) .setLoadBalancer(new RandomLoadBalancer()); for (String slave : slaveList) { URI serverAddress = URI.create(slave); redissonConfig.useMasterSlaveConnection() .addSlaveAddress(serverAddress.getHost() + ":" + serverAddress.getPort()); } if (!authToken.isEmpty()) { redissonConfig.useMasterSlaveConnection() .setPassword(authToken); } } else if (redisType.equals(RedisType.CLUSTER.getStringValue())) { redissonConfig.useClusterServers() .setScanInterval(scanInterval) .addNodeAddress(uri.getHost() + ":" + uri.getPort()); for (String slave : slaveList) { URI serverAddress = URI.create(slave); redissonConfig.useClusterServers() .addNodeAddress(serverAddress.getHost() + ":" + serverAddress.getPort()); } if (!authToken.isEmpty()) { redissonConfig.useClusterServers() .setPassword(authToken); } } else if (redisType.equals(RedisType.SENTINEL.getStringValue())) { String masterName = ""; if (config.getServletConfig().getInitParameter(REDIS_SENTINEL_MASTER_NAME) != null) { masterName = config.getServletConfig().getInitParameter(REDIS_SENTINEL_MASTER_NAME); } else if (masterName.isEmpty()) { throw new NullPointerException("SENTINEL MASTER NAME cannot be null"); } redissonConfig.useSentinelConnection() .setMasterName(masterName) .addSentinelAddress(uri.getHost() + ":" + uri.getPort()); for (String slave : slaveList) { URI serverAddress = URI.create(slave); redissonConfig.useSentinelConnection() .addSentinelAddress(serverAddress.getHost() + ":" + serverAddress.getPort()); } if (!authToken.isEmpty()) { redissonConfig.useSentinelConnection() .setPassword(authToken); } } else if (redisType.equals(RedisType.ELASTICACHE.getStringValue())) { redissonConfig.useElasticacheServers() .addNodeAddress(uri.getHost() + ":" + uri.getPort()) .setScanInterval(scanInterval); if (!authToken.isEmpty()) { redissonConfig.useElasticacheServers() .setPassword(authToken); } } } try { redisson = Redisson.create(redissonConfig); } catch (Exception e) { logger.error("failed to connect redis", e); disconnectRedisson(); } } public void disconnectRedisson() { redisson.shutdown(); } public synchronized void setID(String id) { disconnectRedisson(); } /** * {@inheritDoc} */ public void destroy() { try { disconnectRedisson(); } catch (Throwable t) { logger.warn("Redisson error on close", t); } } /** * {@inheritDoc} */ public void incomingBroadcast() { String callbackId = callback.getID(); logger.info("Subscribing to: {}", callbackId); if (!callbackId.contains("*")) { RTopic<String> topic = redisson.getTopic(callbackId); topic.addListener(new MessageListener<String>() { public void onMessage(String channel, String message) { callback.broadcastReceivedMessage(message); } }); } else { RPatternTopic<Message> topic1 = redisson.getPatternTopic("topic1.*"); topic1.addListener(new PatternMessageListener<Message>() { @Override public void onMessage(String pattern, String channel, Message msg) { callback.broadcastReceivedMessage(msg.toString()); } }); } } public void outgoingBroadcast(Object message) { RTopic<String> topic = redisson.getTopic(callback.getID()); try { topic.publish(message.toString()); } catch (Exception e) { logger.warn("outgoingBroadcast exception", e); } } public static interface Callback { String getID(); void broadcastReceivedMessage(String message); } }