Redisson Connection Listener not getting invoked in version 3.7.0 - redis

I am trying to setup a connection listener for my redisson client. It is not getting invoked on both connect/disconnect.
Tried the code mentioned on the redisson github which is as per below:
public void createRedisClient(Handler<AsyncResult<Redis>> handler) {
ConfigRetriever configRetriever = UDSFBootStrapper.getInstance().getConfigRetriever();
configRetriever.getConfig(
config -> {
String redisUrl = config.result().getString("redisip");
redisUrl += ":";
redisUrl += config.result().getInteger("redisport");
Config rconfig = new Config();
rconfig.setTransportMode(TransportMode.EPOLL);
rconfig.useClusterServers()
.addNodeAddress(UdsfConstants.REDIS_CONNECTION_PREFIX + redisUrl);
rclient = Redisson.create(rconfig);
rclient.getNodesGroup().addConnectionListener(new ConnectionListener() {
//#Override
public void onConnect(InetSocketAddress inetSocketAddress) {
logger.info("Redis server connected");
}
//#Override
public void onDisconnect(InetSocketAddress inetSocketAddress) {
logger.info("Redis server disconnected");
}
});
});
}

Related

Google Cloud Memory Store (Redis), can't connect to redis when instance is just started

I have a problem to connect to redis when my instance is just started.
I use:
runtime: java
env: flex
runtime_config:
jdk: openjdk8
i got following exception:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
java.net.SocketTimeoutException: connect timed out
after 2-3 min, it works smoothly
Do i need to add some check in my code or how i should fix it properly?
p.s.
also i use spring boot, with following configuration
#Value("${spring.redis.host}")
private String redisHost;
#Bean
JedisConnectionFactory jedisConnectionFactory() {
// https://cloud.google.com/memorystore/docs/redis/quotas
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisHost, 6379);
return new JedisConnectionFactory(config);
}
#Bean
public RedisTemplate<String, Object> redisTemplate(
#Autowired JedisConnectionFactory jedisConnectionFactory
) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(newObjectMapper()));
return template;
}
in pom.xml
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.2.RELEASE</version>
I solved this problem as follows: in short, I added the “ping” method, which tries to set and get the value from Redis; if it's possible, then application is ready.
Implementation:
First, you need to update app.yaml add following:
readiness_check:
path: "/readiness_check"
check_interval_sec: 5
timeout_sec: 4
failure_threshold: 2
success_threshold: 2
app_start_timeout_sec: 300
Second, in your rest controller:
#GetMapping("/readiness_check")
public ResponseEntity<?> readiness_check() {
if (!cacheConfig.ping()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().build();
}
Third, class CacheConfig:
public boolean ping() {
long prefix = System.currentTimeMillis();
try {
redisTemplate.opsForValue().set("readiness_check_" + prefix, Boolean.TRUE, 100, TimeUnit.SECONDS);
Boolean val = (Boolean) redisTemplate.opsForValue().get("readiness_check_" + prefix);
return Boolean.TRUE.equals(val);
} catch (Exception e) {
LOGGER.info("ping failed for " + System.currentTimeMillis());
return false;
}
}
P.S.
Also if somebody needs the full implementation of CacheConfig:
#Configuration
public class CacheConfig {
private static final Logger LOGGER = Logger.getLogger(CacheConfig.class.getName());
#Value("${spring.redis.host}")
private String redisHost;
private final RedisTemplate<String, Object> redisTemplate;
#Autowired
public CacheConfig(#Lazy RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
#Bean
JedisConnectionFactory jedisConnectionFactory(
#Autowired JedisPoolConfig poolConfig
) {
// https://cloud.google.com/memorystore/docs/redis/quotas
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisHost, 6379);
JedisClientConfiguration clientConfig = JedisClientConfiguration
.builder()
.usePooling()
.poolConfig(poolConfig)
.build();
return new JedisConnectionFactory(config, clientConfig);
}
#Bean
public RedisTemplate<String, Object> redisTemplate(
#Autowired JedisConnectionFactory jedisConnectionFactory
) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(newObjectMapper()));
return template;
}
/**
* Example: https://github.com/PengliuIBM/pws_demo/blob/1becdca1bc19320c2742504baa1cada3260f8d93/redisData/src/main/java/com/pivotal/wangyu/study/springdataredis/config/RedisConfig.java
*/
#Bean
redis.clients.jedis.JedisPoolConfig jedisPoolConfig() {
final redis.clients.jedis.JedisPoolConfig poolConfig = new redis.clients.jedis.JedisPoolConfig();
// Maximum active connections to Redis instance
poolConfig.setMaxTotal(16);
// Number of connections to Redis that just sit there and do nothing
poolConfig.setMaxIdle(16);
// Minimum number of idle connections to Redis - these can be seen as always open and ready to serve
poolConfig.setMinIdle(8);
// Tests whether connection is dead when returning a connection to the pool
poolConfig.setTestOnBorrow(true);
// Tests whether connection is dead when connection retrieval method is called
poolConfig.setTestOnReturn(true);
// Tests whether connections are dead during idle periods
poolConfig.setTestWhileIdle(true);
return poolConfig;
}
public boolean ping() {
long prefix = System.currentTimeMillis();
try {
redisTemplate.opsForValue().set("readiness_check_" + prefix, Boolean.TRUE, 100, TimeUnit.SECONDS);
Boolean val = (Boolean) redisTemplate.opsForValue().get("readiness_check_" + prefix);
return Boolean.TRUE.equals(val);
} catch (Exception e) {
LOGGER.info("ping failed for " + System.currentTimeMillis());
return false;
}
}
}

akka.net cluster : how to send message from seed node to non-seed node

I am new to akka.net. I have 2 .net core2 console apps in a cluster , trying to send message from actor from one console app [ which is seed node ] to remote actor on another console app [which is non-seed node].
After starting/running both the console apps, the cluster is established and both the nodes are Up and seed node knows about the non-seed node and vice-versa, but no message is received by remote actor that is on the non-seed node. I am creating a round-robin router on the seed-node, but not sure what I am missing?
Please guide.
Below is the sample code of the both the apps i.e seed node and non-seed node.
// .net core2 console App with Seed Node
class Program
{
public static ActorSystem ClusterSystem;
private static IActorRef StartActor;
private static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka
{
actor {
provider=cluster
deployment {
/tasker {
router = round-robin-pool # routing strategy
nr-of-instances = 5 # max number of total routees
cluster {
enabled = on
allow-local-routees = off
use-role = tasker
max-nr-of-instances-per-node = 1
}
}
}
}
remote
{
dot-netty.tcp {
port = 8081
hostname = ""localhost""
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem#localhost:8081""]
roles=[""main""]
}
}
ClusterSystem = ActorSystem.Create("ClusterSystem", config);
var taskActor = ClusterSystem.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), "tasker");
StartActor = ClusterSystem.ActorOf(Props.Create(() => new StartActor(taskActor)), "startactor");
StartActor.Tell(new Initiate()); // call local actor
// actor on seed node (local actor)
class StartActor : ReceiveActor, ILogReceive
{
private IActorRef taskActor;
public StartActor(IActorRef router)
{
this.taskActor = router;
Receive<Initiate>(i => Start(i));
}
private void Start(Initiate initiate)
{
taskActor.Tell(new Initiate()); // calling remote actor
}
}
.net core2 Console app with Non seed node
class Program
{
public static ActorSystem ClusterSystem;
public static IActorRef TaskActor;
private static void Main(string[] args)
{
Console.Title = "BackEnd";
var config = ConfigurationFactory.ParseString(#"
akka
{
actor {
provider=cluster
}
remote
{
dot-netty.tcp {
port = 0
hostname = ""localhost""
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem#localhost:8081""]
roles=[""tasker""]
}
}
");
ClusterSystem = ActorSystem.Create("ClusterSystem", config);
TaskActor = ClusterSystem.ActorOf(Props.Create<TaskActor>(), "tasker");
Console.Read();
}
}
// Actor on Non-seed node (Remote Actor)
class TaskActor : ReceiveActor, ILogReceive
{
private readonly IActorRef manager;
public TaskActor()
{
this.Receive<Initiate>(i => this.Init(i));
}
private void Init(Initiate initiate)
{
Console.WriteLine($"Message Received"); //
}
}
I am myself answering to my question. So the first thing is that since the remote actor is created by/in another console application, the deployment configuration needs to be changed with routing strategy to "round robin group"
/tasker {
router = round-robin-group # routing strategy
routees.paths = [""/user/starter""]
nr-of-instances = 5 # max number of total routees
cluster {
enabled = on
allow-local-routees = off
use-role = tasker
}
}
And the the "startActor" from the seed node need to be as below
class StartActor : ReceiveActor, ILogReceive
{
private IActorRef router, self;
public StartActor(IActorRef router)
{
self = Self;
this.router = router;
Receive<Initiate>(i =>
{
var routee = router.Ask<Routees>(new GetRoutees()).ContinueWith(tr =>
{
if (tr.Result.Members.Count() > 0)
{
Start(i);
}
else
{
self.Tell(i);
}
});
});
}
private void Start(Initiate initiate)
{
router.Tell(initiate);
}
}
The above code within the "startActor" looks for the routees which once received then only the message is sent otherwise the message is blasted and not received by the remote actor.

RabbitMQ MQTT Adapter and Paho MQTT client

I’m using RabbitMQ MQTT Adapter and Paho MQTT client.
RabbitMQ version: {rabbitmq_mqtt,"RabbitMQ MQTT Adapter","3.2.1"}
Paho MQTT client version:
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>RELEASE</version>
</dependency>
Please see code inline.
I’m trying to understand if, the subscriber queue can be durable without expiration time. And If the messages can be durable also.
As I understood from RabbitMQ documentation, each time a subscriber subscribes to a topic
RabbitMQ will create a queue with this naming convention:
mqtt-subscription-<ClientName>qos<ClientQOS>
This queue has an expiration time, how can I create a queue without an expiration time? Can I change this queue expiration time to infinite?
As for now each time I run this command: “service rabbitmq-server restart”
The messages in the queue get deleted.
How can I prevent this? Is there a way I can keep the messages in the queue after restart?
In RabbitMQ management UI, I can see under “Publish message” -> “Delivery mode:” which can be “2-persistent”.
If I use management UI to publish messages with Delivery mode = 2-persistent. The messages will be in the queue after service restart.
How can I achieve the same using Paho MQTT Client?
// Heavily based on RabbitMQ MQTT adapter test case code!
// first, import the RabbitMQ Java client
// and the Paho MQTT client classes, plus any other
// requirements
import com.rabbitmq.client.*;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
// import org.eclipse.paho.client.mqttv3.internal.trace.Trace;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
import javax.net.SocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.*;
/***
* MQTT v3.1 tests
* TODO: synchronise access to variables
*/
public class MqttTestClean implements MqttCallback {
// setup some variables which define where the MQTT broker is
private final String host = "0.0.0.0";
private final int port = 1883;
private final String brokerUrl = "tcp://" + host + ":" + port;
private String clientId;
private String clientId3;
private MqttClient client;
private MqttClient client3;
private MqttConnectOptions conOpt;
private ArrayList<MqttMessage> receivedMessages;
// specify a message payload - doesn't matter what this says, but since MQTT expects a byte array
// we convert it from string to byte array here
private final byte[] payload = "This payload was published on MQTT and read using AMQP.".getBytes();
// specify the topic to be used
private final String topic = "topic/proxy/1.0.0/Report/*";
private int testDelay = 2000;
private long lastReceipt;
private boolean expectConnectionFailure;
private ConnectionFactory connectionFactory;
private Connection conn;
private Channel ch;
// override 10s limit
private class MyConnOpts extends MqttConnectOptions {
private int keepAliveInterval = 60;
#Override
public void setKeepAliveInterval(int keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
}
#Override
public int getKeepAliveInterval() {
return keepAliveInterval;
}
}
public void setUpMqtt() throws MqttException {
clientId = getClass().getSimpleName() + ((int) (10000*Math.random()));
client = new MqttClient(brokerUrl, clientId);
conOpt = new MyConnOpts();
setConOpts(conOpt);
receivedMessages = new ArrayList<MqttMessage>();
expectConnectionFailure = false;
}
public void tearDownMqtt() throws MqttException {
try {
client.disconnect();
} catch (Exception _) {}
}
private void setUpAmqp() throws Exception {
connectionFactory = new ConnectionFactory();
connectionFactory.setHost(host);
conn = connectionFactory.newConnection();
ch = conn.createChannel();
}
private void tearDownAmqp() throws IOException {
conn.close();
}
private void setConOpts(MqttConnectOptions conOpts) {
conOpts.setCleanSession(true);
conOpts.setKeepAliveInterval(60);
}
private void publish(MqttClient client, String topicName, int qos, byte[] payload) throws MqttException {
MqttTopic topic = client.getTopic(topicName);
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
MqttDeliveryToken token = topic.publish(message);
token.waitForCompletion();
}
public void connectionLost(Throwable cause) {
if (!expectConnectionFailure)
System.out.println("Connection unexpectedly lost");
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
lastReceipt = System.currentTimeMillis();
System.out.println("-------------------------------------------------");
System.out.println("------------------" + lastReceipt + "-------------------------------");
System.out.println("------------------" + message.toString() + "-------------------------------");
receivedMessages.add(message);
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
public void run() {
try {
setUpMqtt(); // initialise the MQTT connection
setUpAmqp(); // initialise the AMQP connection
connect();
//String queue = ch.queueDeclare().getQueue();
// String queue = ch.queueDeclare("mqtt-subscription-Snabel-3qos1", true, false, false, null).getQueue();
//ch.queueBind(queue, "amq.topic", "sci-topic.sc.proxy_1393.1.0.0.ApReport.*"/*topic*/);
client.connect(conOpt);
publish(client, "topic/proxy/1.0.0/Report/123456789",1, payload); // publish the MQTT message
client.disconnect();
Thread.sleep(testDelay);
tearDownAmqp(); // cleanup AMQP resources
tearDownMqtt(); // cleanup MQTT resources*/
disConnect();
} catch (Exception mqe) {
mqe.printStackTrace();
}
}
private void connect() throws Exception {
clientId3 = "Test-3";
client3 = new MqttClient(brokerUrl, clientId3);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(false);
client3.connect(connOpts);
client3.setCallback(this);
client3.subscribe(topic);
if(!client3.isConnected()){
System.out.println("Not Connected");
return;
}
System.out.println("Connected");
}
private void disConnect() throws Exception {
try {
client3.disconnect();
} catch (Exception _) {}
}
public static void main(String[] args) {
MqttTest mqt = new MqttTest();
mqt.run();
}
}
This was a RabbitMQ bug:
http://rabbitmq.1065348.n5.nabble.com/MQTT-plugin-message-delivery-mode-td32925.html
It was fixed in:
http://www.rabbitmq.com/release-notes/README-3.2.4.txt

NoInitialContextException in CXF Local Transport for testing the JAX-RS

I am following this tutorial: https://cwiki.apache.org/confluence/display/CXF20DOC/JAXRS+Testing
But I get this error:
javax.naming.NoInitialContextException:Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
This is my local server class:
public class CXFLocalTransportTestSuite {
public static final Logger LOGGER = LogManager.getLogger();
public static final String ENDPOINT_ADDRESS = "local://service0";
private static Server server;
#BeforeClass
public static void initialize() throws Exception {
startServer();
}
private static void startServer() throws Exception {
JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setAddress(ENDPOINT_ADDRESS);
List<Class<?>> resourceClasses = new ArrayList<Class<?>>();
resourceClasses.add(CommunicationWSRESTImpl.class);
factory.setResourceClasses(resourceClasses);
List<ResourceProvider> resourceProviders = new ArrayList<>();
resourceProviders.add(new SingletonResourceProvider(new CommunicationWSRESTImpl()));
factory.setResourceProviders(resourceProviders);
List<Object> providers = new ArrayList<Object>();
providers.add(new JacksonJaxbJsonProvider());
providers.add(new ApiOriginFilter());
providers.add(new AuthenticationFilter());
providers.add(new AuthorizationFilter());
factory.setProviders(providers);
server = factory.create();
server.start();
LOGGER.info("LOCAL TRANSPORT STARTED");
}
#AfterClass
public static void destroy() throws Exception {
server.stop();
server.destroy();
LOGGER.info("LOCAL TRANSPORT STOPPED");
}
}
And a client example:
public class CommunicationApiTest {
// [PUBLIC PROFILE]
// --------------------------------------------------------------------------------------------------------
#Test
public void getLinkedComponentsTest() {
// PATH. PARAM.
// ********************************************************************************************************
String userId = "1";
String componentInstance = "a3449197-cc72-49eb-bc14-5d43a80dfa80";
String portId = "00";
// ********************************************************************************************************
WebClient client = WebClient.create(CXFLocalTransportTestSuite.ENDPOINT_ADDRESS);
client.path("/communication/getLinkedComponents/{userId}-{componentInstance}-{portId}", userId, componentInstance, portId);
client.header("Authorization", "Bearer " + CXFLocalTransportTestSuite.authenticationTokenPublicProfile);
Response res = client.get();
if (null != res) {
assertEquals(StatusCode.SUCCESSFUL_OPERATION.getStatusCode(), res.getStatus());
assertNotNull(res.getEntity());
// VALID RESPONSE
// ********************************************************************************************************
assertEquals("> Modules has not been initialized for userID = 1", res.readEntity(GetLinksResult.class).getMessage());
// ********************************************************************************************************
}
}
}
Finally, this is the jax-rs implementation on the server side:
#Path("/communication")
public class CommunicationWSRESTImpl implements CommunicationWS {
#Path("/getLinkedComponents/{userId}-{componentInstance}-{portId}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getLinkedComponents(
#HeaderParam("Authorization") String accessToken,
#PathParam("userId") String userId,
#PathParam("componentInstance") String componentInstance,
#PathParam("portId") String portId) {
LOGGER.info("[CommunicationWSREST - getLinksComponents] userId: " + userId + " -- componentInstace: "
+ componentInstance + " -- portId: " + portId);
GetLinksResult result = new GetLinksResult();
result.setGotten(false);
result.setPortList(null);
if (userId != null && userId.compareTo("") != 0) {
if (componentInstance != null && componentInstance.compareTo("") != 0) {
if (portId != null && portId.compareTo("") != 0) {
TMM tmm = null;
javax.naming.Context initialContext;
try {
initialContext = new InitialContext();
tmm = (TMM) initialContext.lookup("java:app/cos/TMM");
result = tmm.calculateConnectedPorts(userId, componentInstance, portId);
} catch (Exception e) {
LOGGER.error(e);
result.setMessage("> Internal Server Error");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(result).build();
}
} else {
LOGGER.error("Not found or Empty Port Error");
result.setMessage("> Not found or Empty Port Error");
return Response.status(Status.NOT_FOUND).entity(result).build();
}
} else {
LOGGER.error("Not found or Empty Component Instance Error");
result.setMessage("> Not found or Empty Component Instance Error");
return Response.status(Status.NOT_FOUND).entity(result).build();
}
} else {
LOGGER.error("Not found or Empty userid Error");
result.setMessage("> Not found or Empty username Error");
return Response.status(Status.NOT_FOUND).entity(result).build();
}
return Response.ok(result).build();
}
}
Maybe the problem is the local transport is not correctly configured what launches the exception because of the lookup (see: server side):
TMM tmm = null;
javax.naming.Context initialContext;
try {
initialContext = new InitialContext();
tmm = (TMM) initialContext.lookup("java:app/cos/TMM");
result = tmm.calculateConnectedPorts(userId, componentInstance, portId);
} catch (Exception e) {
..
The problem is most likely because you are running your test in a Java SE environment that is not configured with a JNDI server. If you run your test as part of a WAR inside a Java EE app server, this would probably work just fine.
So you might need to either run your unit test inside an app server or you could try mocking a JNDI server like what is described here: http://en.newinstance.it/2009/03/27/mocking-jndi/#
Hope this helps,
Andy

How to authenticate with mbean bypassing jmx.access file when using JAAS module

I amfacing one issue with mbean authentication. Issue is i need to always change my mbean jmx.access file to match with different users for authorization rule. Somehow i need to bypass this jmx.access file and authenticate using my custom JAAS login module only which call the rest api at backend.
Please suggest.
Also to do this any other approach better than this is appreciated!
Here is my all code
public class SystemConfigManagement {
private static final int DEFAULT_NO_THREADS = 10;
private static final String DEFAULT_SCHEMA = "default";
private static String response = null;
public static void main(String[] args) throws MalformedObjectNameException, InterruptedException,
InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
// Get the MBean server
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// register the MBean
SystemConfig mBean = new SystemConfig(DEFAULT_NO_THREADS, DEFAULT_SCHEMA);
ObjectName name = new ObjectName("com.sigma.jmx:type=SystemConfig");
mbs.registerMBean(mBean, name);
do {
Thread.sleep(3000);
System.out.println("Thread Count=" + mBean.getThreadCount() + ":::Schema Name="
+ mBean.getSchemaName());
if (mBean.getSchemaName().equalsIgnoreCase("NewSchema")) {
System.out.println("Yes, you got right shcema name with token " + mBean.getToken());
response = RestClient.callPost("/validate-token", mBean.getToken(), "{}");
System.out.println("Toekn validation response " + response);
if (response.contains("\"valid\":true")) {
System.out.println("You are Logged In....");
} else {
System.out.println("Your Token is invalid, you cannot login...");
}
} else {
System.out.println("Schema name is invalid");
}
} while (mBean.getThreadCount() != 0);
}
}
JAAS login Module
package com.sigma.loginmodule;
import java.util.*;
import java.io.IOException;
import javax.management.remote.JMXPrincipal;
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.spi.*;
import com.sigma.loginmodule.SamplePrincipal;
public class SampleLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
// configurable option
private boolean debug = false;
private boolean succeeded = false;
private boolean commitSucceeded = false;
// username and password
private String username;
private char[] password;
private JMXPrincipal user;
// testUser's SamplePrincipal
private SamplePrincipal userPrincipal;
public SampleLoginModule() {
System.out.println("Login Module - constructor called");
}
public boolean abort() throws LoginException {
System.out.println("Login Module - abort called");
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
if (password != null) {
for (int i = 0; i < password.length; i++)
password[i] = ' ';
password = null;
}
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
// return false;
}
public boolean commit() throws LoginException {
System.out.println("Login Module - commit called");
subject.getPrincipals().add(user);
return succeeded;
}
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {
System.out.println("Login Module - initialize called");
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
// System.out.println("testOption value: " + (String) options.get("testOption"));
debug = "true".equalsIgnoreCase((String) options.get("debug"));
succeeded = false;
}
public boolean login() throws LoginException {
System.out.println("Login Module - login called");
if (callbackHandler == null) {
throw new LoginException("Oops, callbackHandler is null");
}
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("name:");
callbacks[1] = new PasswordCallback("password:", false);
try {
callbackHandler.handle(callbacks);
} catch (IOException e) {
throw new LoginException("Oops, IOException calling handle on callbackHandler");
} catch (UnsupportedCallbackException e) {
throw new LoginException("Oops, UnsupportedCallbackException calling handle on callbackHandler");
}
NameCallback nameCallback = (NameCallback) callbacks[0];
PasswordCallback passwordCallback = (PasswordCallback) callbacks[1];
String name = nameCallback.getName();
String password = new String(passwordCallback.getPassword());
if ("sohanb".equals(name) && "welcome".equals(password)) {
System.out.println("Success! You get to log in!");
user = new JMXPrincipal(name);
succeeded = true;
return succeeded;
} else {
System.out.println("Failure! You don't get to log in");
succeeded = false;
throw new FailedLoginException("Sorry! No login for you.");
}
// return true;
}
public boolean logout() throws LoginException {
System.out.println("Login Module - logout called");
return false;
}
}
JMX client code :
package client;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import com.sigma.SystemConfigMBean;
public class SystemConfigClient {
public static final String HOST = "localhost";
public static final String PORT = "8888";
public static void main(String[] args) throws IOException, MalformedObjectNameException {
JMXServiceURL url =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + HOST + ":" + PORT + "/jmxrmi");
//service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi
// for passing credentials for password
Map<String, String[]> env = new HashMap<>();
String[] credentials = { "sohanb", "welcome" };
env.put(JMXConnector.CREDENTIALS, credentials);
JMXConnector jmxConnector = JMXConnectorFactory.connect(url,env);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
//ObjectName should be same as your MBean name
ObjectName mbeanName = new ObjectName("com.sigma.jmx:type=SystemConfig");
//Get MBean proxy instance that will be used to make calls to registered MBean
SystemConfigMBean mbeanProxy =
(SystemConfigMBean) MBeanServerInvocationHandler.newProxyInstance(
mbeanServerConnection, mbeanName, SystemConfigMBean.class, true);
//let's make some calls to mbean through proxy and see the results.
System.out.println("Current SystemConfig::" + mbeanProxy.doConfig());
String autenticate = RestClient.authenticate("handong", "welcome", true);
System.out.println("Got autenticate Toekn id as " + autenticate);
mbeanProxy.setToken(autenticate);
mbeanProxy.setSchemaName("NewSchema");
mbeanProxy.setThreadCount(5);
System.out.println("New SystemConfig::" + mbeanProxy.doConfig());
//let's terminate the mbean by making thread count as 0
// mbeanProxy.setThreadCount(0);
//close the connection
jmxConnector.close();
}
}
Sample JAAS file:
Sample {
com.sigma.loginmodule.SampleLoginModule required debug=true ;
};
I can see only way to resolve this is to write your own custom JAAS autheticator which implements JMXAuthenticator .
Code snippet of my main authenticate method used for authentication.
This method call invoke my login module passed in constructor of JAAS authenticator,
#SuppressWarnings("unchecked")
public final Subject authenticate(final Object credentials) throws SecurityException {
Map<String, Object> myCredentials = new HashMap<String, Object>();
if (credentials instanceof String[]) {
// JConsole sends the credentials as string array
// credentials[0] is the username
// credentials[1] is the password
String[] args = (String[]) credentials;
if (args.length == 2) {
myCredentials.put(USERNAME, args[0]);
char[] pw = null;
if (args[1] != null) {
pw = args[1].toCharArray();
}
myCredentials.put(PASSWORD, pw);
} else {
throw new SecurityException();
}
} else if (credentials instanceof Map) {
myCredentials.putAll((Map) credentials);
if (sslEnabled && myCredentials.containsKey(CERTIFICATE)) {
throw new SecurityException();
}
} else {
throw new SecurityException();
}
LoginContext lc = null;
try {
lc = new LoginContext(systemName, new CredentialCallbackHandler(systemName, myCredentials));
System.out.println("JAAS authenticator called ...");
} catch (LoginException le) {
le.printStackTrace();
}
try {
lc.login();
try {
Subject.doAsPrivileged(lc.getSubject(), new PrintCodeBaseAndPrincipalsAction(), null);
} catch (PrivilegedActionException ex) {
if (ex.getException() instanceof SecurityException) {
throw (SecurityException) ex.getException();
} else {
throw new SecurityException(ex.getException());
}
}
return lc.getSubject();
} catch (LoginException ex) {
throw new SecurityException(ex);
} catch (SecurityException ex) {
throw ex;
} catch (Throwable ex) {
throw new SecurityException(ex);
}
}
Here is how i invoke and set my JAAS authenticator constructor ,
Map<String, Object> env = new HashMap<String, Object>();
JAASJMXAuthenticator authenticator = new JAASJMXAuthenticator(jaasConfigName, false);
if (authenticator != null) {
System.out.println("JAASJMXAuthenticator is not null");
env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
}
Hope this helps someone in future. I can provide full code sample if asked.
Cheers!