I am trying to get the name and state of the servers in a domain using MBeanServerConnection
public class GetServerState {
private static MBeanServerConnection connection;
private static JMXConnector connector;
private static final ObjectName service;
// Initializing the object name for DomainRuntimeServiceMBean
// so it can be used throughout the class.
static {
try {
service = new ObjectName(
"com.bea:Name=DomainRuntimeService,Type=weblogic.management.
mbeanservers.domainruntime.DomainRuntimeServiceMBean");
}catch (MalformedObjectNameException e) {
throw new AssertionError(e.getMessage());
}
}
/*
* Initialize connection to the Domain Runtime MBean Server
*/
public static void initConnection(String hostname, String portString,
String username, String password) throws IOException,
MalformedURLException {
String protocol = "t3";
Integer portInteger = Integer.valueOf(portString);
int port = portInteger.intValue();
String jndiroot = "/jndi/";
String mserver = "weblogic.management.mbeanservers.domainruntime";
JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname,
port, jndiroot + mserver);
Hashtable h = new Hashtable();
h.put(Context.SECURITY_PRINCIPAL, username);
h.put(Context.SECURITY_CREDENTIALS, password);
h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,
"weblogic.management.remote");
connector = JMXConnectorFactory.connect(serviceURL, h);
connection = connector.getMBeanServerConnection();
}
/*
* Print an array of ServerRuntimeMBeans.
* This MBean is the root of the runtime MBean hierarchy, and
* each server in the domain hosts its own instance.
*/
public static ObjectName[] getServerRuntimes() throws Exception {
return (ObjectName[]) connection.getAttribute(service,
"ServerRuntimes");
}
/*
* Iterate through ServerRuntimeMBeans and get the name and state
*/
public void printNameAndState() throws Exception {
ObjectName[] serverRT = getServerRuntimes();
System.out.println("got server runtimes");
int length = (int) serverRT.length;
for (int i = 0; i < length; i++) {
String name = (String) connection.getAttribute(serverRT[i],
"Name");
String state = (String) connection.getAttribute(serverRT[i],
"State");
System.out.println("Server name: " + name + ". Server state: "
+ state);
}
}
public static void main(String[] args) throws Exception {
String hostname = args[0];
String portString = args[1];
String username = args[2];
String password = args[3];
GetServerState s = new GetServerState();
initConnection(hostname, portString, username, password);
s.printNameAndState();
connector.close();
}
}
Here I am getting the name of only those servers which are in "RUNNING" state and not the list of all the servers in the Domain.
Can some one guide me with what changes I need to make to get name and states of all the server in the domain?
Related
Good Day,
I am struggling with below error when trying to connect to IBM MQ. Unable to access createContext(); and forces me to use (JMSContext) cf.createConnection(); which is resulting me error as below:
"Exception in thread "main" java.lang.ClassCastException: com.ibm.mq.jms.MQConnection cannot be cast to javax.jms.JMSContext
at pushmsgs.main(pushmsgs.java:55)"
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
import javax.jms.*;
public class pushmsgs {
// System exit status value (assume unset value to be 1)
private static int status = 1;
// Create variables for the connection to MQ
private static final String HOST = "22.188.133.100"; // Host name or IP address
private static final int PORT = 3415; // Listener port for your queue manager
private static final String CHANNEL = "DEV.APP.SVRCONN"; // Channel name
private static final String QMGR = "SITQUEUEMGR"; // Queue manager name
// private static final String APP_USER = "app"; // User name that application uses to connect to MQ
//private static final String APP_PASSWORD = "_APP_PASSWORD_"; // Password that the application uses to connect to MQ
private static final String QUEUE_NAME = "TESTQUEUE.MQAPP.REQ.RCV"; // Queue that the application uses to put and get messages to and from
/**
* Main method
*
* #param args
*/
public static void main(String[] args) {
// Variables
JMSContext context = null;
Destination destination = null;
JMSProducer producer = null;
JMSConsumer consumer = null;
try {
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
// Set the properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, HOST);
cf.setIntProperty(WMQConstants.WMQ_PORT, PORT);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, CHANNEL);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QMGR);
// cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "JmsPutGet (JMS)");
//cf.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
// cf.setStringProperty(WMQConstants.USERID, APP_USER);
//cf.setStringProperty(WMQConstants.PASSWORD, APP_PASSWORD);
//cf.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "*TLS12");
// Create JMS objects
context = (JMSContext) cf.createConnection();
destination = context.createQueue("queue:///" + QUEUE_NAME);
long uniqueNumber = System.currentTimeMillis() % 1000;
TextMessage message = context.createTextMessage("Your lucky number today is " + uniqueNumber);
producer = context.createProducer();
producer.send(destination, message);
System.out.println("Sent message:\n" + message);
context.close();
recordSuccess();
} catch (JMSException jmsex) {
recordFailure(jmsex);
}
System.exit(status);
} // end main()
/**
* Record this run as successful.
*/
private static void recordSuccess() {
System.out.println("SUCCESS");
status = 0;
return;
}
/**
* Record this run as failure.
*
* #param ex
*/
private static void recordFailure(Exception ex) {
if (ex != null) {
if (ex instanceof JMSException) {
processJMSException((JMSException) ex);
} else {
System.out.println(ex);
}
}
System.out.println("FAILURE");
status = -1;
return;
}
/**
* Process a JMSException and any associated inner exceptions.
*
* #param jmsex
*/
private static void processJMSException(JMSException jmsex) {
System.out.println(jmsex);
Throwable innerException = jmsex.getLinkedException();
if (innerException != null) {
System.out.println("Inner exception(s):");
}
while (innerException != null) {
System.out.println(innerException);
innerException = innerException.getCause();
}
return;
}
}
Added JMS mvn dependency and com.ibm.mq.allclient dependency
It's because you are assigning the wrong object to the wrong variable.
context = (JMSContext) cf.createConnection();
I don't know why you would want to do that. A connection is not a context.
It should be:
Connection conn = cf.createConnection("MyUserId", "mypassword");
See my example called MQTestJMS51 here.
I have an issue with connecting to wildfly remote queue.
My MDB on wildfly instance is:
#MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "queue/HELLOWORLDMDBQueue"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
public class HelloWorldQueueMDB implements MessageListener {
private static final Logger LOGGER = Logger.getLogger(HelloWorldQueueMDB.class.toString());
/**
* #see MessageListener#onMessage(Message)
*/
public void onMessage(Message rcvMessage) {
TextMessage msg = null;
try {
if (rcvMessage instanceof TextMessage) {
msg = (TextMessage) rcvMessage;
LOGGER.info("Received Message from queue: " + msg.getText());
} else {
LOGGER.warning("Message of wrong type: " + rcvMessage.getClass().getName());
}
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
}
and the standalone client is:
public class HelloWorldJMSClient {
private static final Logger log = Logger.getLogger(HelloWorldJMSClient.class.getName());
// Set up all the default values
private static final String DEFAULT_MESSAGE = "Hello, World!";
private static final String DEFAULT_CONNECTION_FACTORY = "jms/RemoteConnectionFactory";
private static final String DEFAULT_DESTINATION = "queue/HELLOWORLDMDBQueue";
private static final String DEFAULT_MESSAGE_COUNT = "1";
private static final String DEFAULT_USERNAME = "quickstartUser";
private static final String DEFAULT_PASSWORD = "quickstartPwd1!";
private static final String INITIAL_CONTEXT_FACTORY = "org.wildfly.naming.client.WildFlyInitialContextFactory";
private static final String PROVIDER_URL = "http-remoting://127.0.0.1:8080";
public static void main(String[] args) {
Context namingContext = null;
try {
String userName = System.getProperty("username", DEFAULT_USERNAME);
String password = System.getProperty("password", DEFAULT_PASSWORD);
// Set up the namingContext for the JNDI lookup
final Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, PROVIDER_URL));
env.put(Context.SECURITY_PRINCIPAL, userName);
env.put(Context.SECURITY_CREDENTIALS, password);
namingContext = new InitialContext(env);
// Perform the JNDI lookups
String connectionFactoryString = System.getProperty("connection.factory", DEFAULT_CONNECTION_FACTORY);
log.info("Attempting to acquire connection factory \"" + connectionFactoryString + "\"");
ConnectionFactory connectionFactory = (ConnectionFactory) namingContext.lookup(connectionFactoryString);
log.info("Found connection factory \"" + connectionFactoryString + "\" in JNDI");
String destinationString = System.getProperty("destination", DEFAULT_DESTINATION);
log.info("Attempting to acquire destination \"" + destinationString + "\"");
Destination destination = (Destination) namingContext.lookup(destinationString);
log.info("Found destination \"" + destinationString + "\" in JNDI");
int count = Integer.parseInt(System.getProperty("message.count", DEFAULT_MESSAGE_COUNT));
String content = System.getProperty("message.content", DEFAULT_MESSAGE);
try (JMSContext context = connectionFactory.createContext(userName, password)) {
log.info("Sending " + count + " messages with content: " + content);
// Send the specified number of messages
for (int i = 0; i < count; i++) {
context.createProducer().send(destination, content);
}
// Create the JMS consumer
JMSConsumer consumer = context.createConsumer(destination);
// Then receive the same number of messages that were sent
for (int i = 0; i < count; i++) {
String text = consumer.receiveBody(String.class, 5000);
log.info("Received message with content " + text);
}
}
} catch (NamingException e) {
log.severe(e.getMessage());
} finally {
if (namingContext != null) {
try {
namingContext.close();
} catch (NamingException e) {
log.severe(e.getMessage());
}
}
}
}
}
And the error output is:
pro 10, 2020 1:49:20 PM org.jboss.as.quickstarts.jms.HelloWorldJMSClient main
INFO: Found connection factory "jms/RemoteConnectionFactory" in JNDI
pro 10, 2020 1:49:20 PM org.jboss.as.quickstarts.jms.HelloWorldJMSClient main
INFO: Attempting to acquire destination "queue/HELLOWORLDMDBQueue"
pro 10, 2020 1:49:20 PM org.jboss.as.quickstarts.jms.HelloWorldJMSClient main
SEVERE: queue -- service jboss.naming.context.java.jboss.exported.queue
I'm using wildfly 21 and I don't know what I'm doing wrong.
In the wildfly management console I can see the MDB:
URI
java:/queue/HELLOWORLDMDBQueue
Class Name
org.apache.activemq.artemis.jms.client.ActiveMQQueue
Value
ActiveMQQueue[jms.queue.HelloWorldMDBQueue]
Update:
Executed:
jms-queue add --queue-address=HelloWorldQueueMDB --entries=queue/HelloWorldQueueMDB,java:jboss/exported/jms/queue/HelloWorldQueueMDB
on wildfly side I got:
15:05:27,427 ERROR [org.apache.activemq.artemis.core.client] (default I/O-53) AMQ214013: Failed to decode packet: java.lang.NoClassDefFoundError: java/security/acl/Group
Update:
java/security/acl/Group was removed from java14, so using an older version of java works.
So you are trying to access a Queue remotely and not an MDB. Your queue should be exposed externally using the jboss/exported namespace.
java:jboss/exported/queue/HELLOWORLDMDBQueue would work and make the queue available.
You can take a look at WildFly HelloworlJms quickstart or HelloworldMdb for such an example
I have a very simple Java application that sends messages to an openfire server using a custom account called "thesis.dimfccs.sensors", who is seding a message to another custom account called "thesis.dimfccs".
I am using openfire 3.9.1 and my Java application is able to connect to the server, but it is unable to login into it. When I try to login, I get the following error:
Initializing connection to server localhost, port 5222, service gmail.com
Connected: true
Logging in: thesis.dimfccs.sensors, thesis.dimfccs.sensors.pwd
Exception in thread "main" No response from the server.:
at org.jivesoftware.smack.NonSASLAuthentication.authenticate(NonSASLAuthentication.java:73)
at org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:357)
at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:243)
at org.jivesoftware.smack.Connection.login(Connection.java:366)
at javacodegeeks.XmppManager.performLogin(XmppManager.java:76)
at javacodegeeks.XmppLocalTest.main(XmppLocalTest.java:44)
I know that the password and name for the test account are correct because I can enter the Spark IM client using them.
However, what really blows my mind, is that if I do the exact same thing using the "admin" account, everything works!
I have double checked the thesis.dimfccs.sensors password and gave it admin access, but it is still not enough.
Here is the code:
Main.java:
public class XmppLocalTest {
//Total number of messages sent during one minute.
public static final int MESSAGES_NUMBER = 120;
//Each message will have a random number between 0 and MAX_RANDOM.
public static final int MAX_RANDOM = 100;
private static final String SENDER_USERNAME = "thesis.dimfccs.sensors";
private static final String SENDER_PASSWORD = "thesis.dimfccs.sensors.pwd";
public static void main(String[] args) throws Exception {
String username = SENDER_USERNAME;
String password = SENDER_PASSWORD;
XmppManager xmppManager = new XmppManager("localhost", 5222,
"gmail.com");
xmppManager.init();
xmppManager.performLogin(username, password);
System.out.println("Login performed with success, setting status.");
xmppManager.setStatus(true, "Let the pain begin!");
System.out.println("Setting up receivers");
xmppManager.createEntry("thesis.dimfccs", "thesis.dimfccs");
//go message!
xmppManager.sendMessage("Hello world!", "thesis.dimfccs#gmail.com");
xmppManager.destroy();
}
}
XmppManager.java:
package javacodegeeks;
/**
* Main class with all the logic of the connection. Followed from the tutorials:
* http://www.javacodegeeks.com/2010/09/xmpp-im-with-smack-for-java.html
* */
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
public class XmppManager {
private static final int packetReplyTimeout = 500; // millis
private String server;
private int port;
private String service;
private ConnectionConfiguration config;
private XMPPConnection connection;
private ChatManager chatManager;
private MessageListener messageListener;
public XmppManager(String aServer, int aPort, String aService) {
server = aServer;
port = aPort;
service = aService;
}
public XmppManager(String server, int port) {
this(server, port, null);
}
public void init() throws XMPPException {
System.out.println(String.format("Initializing connection to server " + server + ", port " + port
+ ", service " + service));
SmackConfiguration.setPacketReplyTimeout(packetReplyTimeout);
if(service != null)
config = new ConnectionConfiguration(server, port, service);
else
config = new ConnectionConfiguration(server, port);
// config.setSASLAuthenticationEnabled(false);
// config.setSecurityMode(SecurityMode.disabled);
// SASLAuthentication.supportSASLMechanism("PLAIN", 0);
config.setSASLAuthenticationEnabled(true);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
connection = new XMPPConnection(config);
connection.connect();
System.out.println("Connected: " + connection.isConnected());
chatManager = connection.getChatManager();
messageListener = new MyMessageListener();
}
public void performLogin(String username, String password) throws XMPPException {
if (connection!=null && connection.isConnected()) {
System.out.println("Logging in: " + username + ", " + password);
connection.login(username, password);
}
}
public void setStatus(boolean available, String status) {
Presence.Type type = available? Type.available: Type.unavailable;
Presence presence = new Presence(type);
presence.setStatus(status);
connection.sendPacket(presence);
}
public void destroy() {
if (connection!=null && connection.isConnected()) {
connection.disconnect();
}
}
public void sendMessage(String message, String buddyJID) throws XMPPException {
System.out.println(String.format("Sending message " + message + " to user " + buddyJID));
Chat chat = chatManager.createChat(buddyJID, messageListener);
chat.sendMessage(message);
}
public void createEntry(String user, String name) throws Exception {
System.out.println(String.format("Creating entry for buddy " + user + " with name " + name));
Roster roster = connection.getRoster();
roster.createEntry(user, name, null);
}
private class MyMessageListener implements MessageListener {
#Override
public void processMessage(Chat chat, Message message) {
String from = message.getFrom();
String body = message.getBody();
System.out.println(String.format("Received message " + body + " from " + from));
}
}
}
What am I doing wrong? How can I fix this?
NOTE: If you believe this question is poor, please leave a comment and suggest an edit instead of down-voting it. This way I can actually improve the question and everyone gets happy!
The problem was that my server seemed to be inconsistent somehow. After deleting and re.creating the users, the problem got fixed !
I have installed OpenX-2.8.10 on Web Server.
I'm using samples from it to connect to OpenX Server. I want to get Advertiser from server, and I have problem with it. I'm trying use AdvertiserService, but no success.
Code is here:
public class Prototype {
private final static String serverURL = "http://demo.pwi.ru";
private final static String openadsDir = "/openx";
private final static String logonService = "/www/api/v1/xmlrpc/LogonXmlRpcService.php";
private final static String agencyService = "/www/api/v1/xmlrpc/AgencyXmlRpcService.php";
private final static String username = "admin";
private final static String password = "875698";
private static Integer id = 1;
public static void main(String[] args) {
final XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
try {
config.setServerURL(new URL(serverURL + openadsDir + logonService));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
String sessionId = (String) client.execute("logon", new Object[]{username, password});
System.out.println("User logged on with session Id: " + sessionId);
AdvertiserService service = new AdvertiserService(client, sessionId);
service.setSessionId(sessionId);
System.out.println("AdvertiserService: " + service.getAdvertiser(id ));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlRpcException e) {
e.printStackTrace();
}
}
}
Console says:
User logged on with session Id: phpads50addd559301e5.24695272
org.apache.xmlrpc.XmlRpcException: Failed to create input stream: demo.pwi.ru//phpads50addd559301e5.24695272//AdvertiserXmlRpcService.php
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.getInputStream(XmlRpcSunHttpTransport.java:65)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:141)
at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:94)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:44)
at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:53)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:166)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:136)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:125)
at org.openads.proxy.AbstractService.execute(AbstractService.java:111)
at org.openads.proxy.AdvertiserService.execute(AdvertiserService.java:1)
at org.openads.proxy.AdvertiserService.getAdvertiser(AdvertiserService.java:118)
at org.openads.proxy.prototype.Prototype.main(Prototype.java:36)
Caused by: java.io.FileNotFoundException: demo.pwi.ru//phpads50addd559301e5.24695272//AdvertiserXmlRpcService.php
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.getInputStream(XmlRpcSunHttpTransport.java:63)
... 11 more
Caused by:
java.io.FileNotFoundException: demo.pwi.ru//phpads50addd559301e5.24695272//AdvertiserXmlRpcService.php
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.getInputStream(XmlRpcSunHttpTransport.java:63)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:141)
at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:94)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:44)
at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:53)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:166)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:136)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:125)
at org.openads.proxy.AbstractService.execute(AbstractService.java:111)
at org.openads.proxy.AdvertiserService.execute(AdvertiserService.java:1)
at org.openads.proxy.AdvertiserService.getAdvertiser(AdvertiserService.java:118)
at org.openads.proxy.prototype.Prototype.main(Prototype.java:36)
How to get List of LDAP servers given a domain name (using java + acitvedirectory) , and authenticate it based on username and password ?
Here are the steps to do it
GET Server List
Hit DNS server to get SRV records
Sort SRV records
Filter server record based on regexp pattern
Construct LDAP urls based on if we need ssl or not. Note that srv record can return only one port so dont rely on port returned from srv record. See srv record RFC
Authentication
iterate through list of servers
Construct Hashtable with ldap environments. note that need to append domain name with username separated by \, and add ldap URL to it
try to create InitialDirContext
success : close context and return
failure1 : if naming exception is AuthenticationException and contains messge "[LDAP: error code 49" translate and throw a readable exception ! see error code mapping
failure2 : if not failure1 continue with next ldap URL
And Here is the full fledged working code
If you are using spring use following conf to invoke init method
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass">
<value>org.bhate.ldap.LdapUtil</value>
</property>
<property name="targetMethod" value="init" />
<property name="arguments">
<list>
<value>${ldap.dnsServer}</value>
<value>${ldap.domainName}</value>
<value>${ldap.filter.regexp}</value>
<value>${ldap.ssl}</value>
</list>
</property>
And Following properties
ldap.dnsServer=uk.mydomain.com
ldap.domainName=DOMAINNAME
ldap.filter.regexp=serv10.*|server.*
ldap.ssl=true
else call init during startup
LdapUtil.init("uk.mydomain.com", "DOMAINNAME", "serv10.*|server.*",true);
LdapUtil.java
package org.bhate.ldap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.TreeSet;
import java.util.regex.Pattern;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.spi.NamingManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.security.BadCredentialsException;
public class LdapUtil {
private static final Logger LOGGER = LoggerFactory
.getLogger(LdapUtil.class);
#SuppressWarnings("serial")
final static Map<String, String> errorCodesMap = new HashMap<String, String>() {
{
put("525", "user not found");
put("52e", "invalid credentials");
put("530", "not permitted to logon at this time");
put("531", "not permitted to logon at this workstation");
put("532", "password expired");
put("533", "account disabled");
put("701", "account expired");
put("773", "user must reset password");
put("775", "user account locked");
}
};
private static Collection<String> ldapServers;
private static String domainName;
private static Pattern PATTERN;
public static DirContext getDirContext(String url, String domainName,
String userName, String password) throws NamingException {
Hashtable<String, Object> env = getEnv(url, domainName, userName,
password);
return new InitialDirContext(env);
}
public static Collection<String> getLdapServers(String ldapDomain,
boolean useSsl) throws NamingException {
Collection<String> serverRecords = getSRVRecords(ldapDomain);
serverRecords = reOrder(serverRecords);
Collection<String> serverNames = new LinkedHashSet<String>();
String protocol = "ldap" + (useSsl ? 's' : "");
for (String s : serverRecords) {
String hostName = s.substring(s.lastIndexOf(' ') + 1,
s.length() - 1);
serverNames.add(protocol + "://" + hostName);
}
return serverNames;
}
private static Collection<String> reOrder(Collection<String> serverRecords) {
return serverRecords;
}
private static Collection<String> getSRVRecords(String ldapDomain)
throws NamingException {
DirContext context = (DirContext) NamingManager.getURLContext("dns",
new Hashtable<String, Object>());
String ldapDNSUrl = "dns:///_ldap._tcp." + ldapDomain;
String[] attrIds = { "SRV" };
Attributes attributes = context.getAttributes(ldapDNSUrl, attrIds);
Attribute servers = attributes.get("SRV");
int L = servers.size();
Collection<String> serverRecords = new TreeSet<String>();
for (int i = 0; i < L; i++) {
String s = (String) servers.get(i);
if (PATTERN.matcher(s).find())
serverRecords.add(s);
}
return serverRecords;
}
public static void authenticate(String userName, String password) {
String msg = "Unable authenticate user {} with {}";
for (String url : ldapServers) {
DirContext ctx = null;
try {
ctx = getDirContext(url, domainName, userName, password);
LOGGER
.info("Authenticated user {} on server {}", userName,
url);
return;
} catch (NamingException e) {
LOGGER.error(msg, userName, url);
String m = NestedExceptionUtils.buildMessage(e.getMessage(), e
.getCause());
LOGGER.error(m, e);
if (e instanceof AuthenticationException
&& e.getMessage().startsWith("[LDAP: error code 49"))
throwMeanigfulEx(userName, url, e);
} finally {
close(ctx);
}
}
LOGGER.error(msg, userName, "any available server");
throw new BadCredentialsException(
"Unable to authenticate : Please contact application support team");
}
public static void init(String dnsServerName, String domainName,
String serverFilter, boolean useSsl) {
PATTERN = Pattern.compile(serverFilter);
try {
LdapUtil.domainName=domainName;
ldapServers = getLdapServers(dnsServerName, useSsl);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(
"LDAP servers available in domain {} to connect {}",
dnsServerName, ldapServers);
}
} catch (NamingException e) {
throw new RuntimeException("Unable retrieve ldapServers for "
+ dnsServerName, e);
}
}
private static void throwMeanigfulEx(String userName, String url,
NamingException e) {
String separator = ", data ";
String m = e.getMessage();
int strt = m.lastIndexOf(separator) + separator.length();
int end = m.lastIndexOf(", vece");
String code = m.substring(strt, end);
throw new BadCredentialsException("Unable to authenticate : "
+ errorCodesMap.get(code));
}
private static void close(DirContext ctx) {
if (ctx != null)
try {
ctx.close();
} catch (NamingException e) {
LOGGER.error("Unable to close context", e);
}
}
private static Hashtable<String, Object> getEnv(String url,
String domainName, String userName, String password) {
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.REFERRAL, "follow");
env.put(Context.PROVIDER_URL, url);
env.put("com.sun.jndi.ldap.connect.timeout", "2000");
// env.put("com.sun.jndi.ldap.trace.ber", System.err);
// env.put("javax.net.ssl.trustStoreType", "JKS");
// env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, getPrincipal(domainName, userName));
env.put(Context.SECURITY_CREDENTIALS, password);
return env;
}
private static Object getPrincipal(String domainName, String userName) {
return domainName + "\\" + userName;
}
public static void main(String[] args) throws NamingException {
init("uk.mydomain.com", "DOMAINNAME", "serv10.*|server.*",true);
System.out.println(ldapServers.size());
System.out.println(ldapServers);
if (args.length == 2)
authenticate(args[0], args[1]);
}
//
// public static InitialLdapContext getLdapContext(String userName,
// String domainName, String password, String url)
// throws NamingException {
// Hashtable<String, Object> env = getEnv(url, domainName, userName,
// password);
// Control[] connCtls = new Control[] { new Control() {
// private static final long serialVersionUID = 1L;
//
// public byte[] getEncodedValue() {
// return null;
// }
//
// public String getID() {
// return "1.2.840.113556.1.4.1781";
// }
//
// public boolean isCritical() {
// return true;
// }
// } };
// return new InitialLdapContext(env, connCtls);
// }
}
Here is a snippet for looking up SRV records in Java, use it with the domain given by ShaMan (_ldap._tcp.dc._msdcs.your.domain.com).
private static final String[] SRV = new String[] { "SRV" };
public static Collection<InetSocketAddress> srv(String name)
throws NamingException
{
DirContext ctx = new IntialDirContext();
Attributes attrs = ctx.getAttributes("dns:/" + name, SRV);
if(attributes.get("SRV") == null)
{
return Collections.emptyList();
}
NamingEnumeration<?> e = attributes.get("SRV").getAll();
TreeMap<Integer, InetSocketAdress> result = new TreeMap<Integer, InetSocketAdress>();
while(e.hasMoreElements())
{
String line = (String) e.nextElement();
// The line is priority weight port host
String[] parts = line.split("\\s+");
int prio = Integer.parseInt(parts[0]);
int port = Integer.parseInt(parts[2]);
String host = parts[3];
result.put(prio, new InetSocketAddress(host, port));
}
return result.values();
}
You can get a list of LDAP servers for a given AD domain by reading the SRV records for that domain. The SRV record you need have value similar to this one _ldap._tcp.dc._msdcs.your.domain.com. This article should give you some more information: http://technet.microsoft.com/en-us/library/cc738991%28WS.10%29.aspx.
You can use JNDI to retrieve the DNS information and later to authenticate via LDAP. A nice tutorial can be found here: http://download.oracle.com/javase/jndi/tutorial/.