I'm pretty sure i know the problem, I just don't know how to solve it.
I have a java EE application that do searches in ldap. I initialize the context with getEnv :
(note* : code is a bit simplified for understanding purposes)
InitialDirContext ctx = new InitialDirContext( getEnv( CONFIG_MAP ); //CONFIG_MAP contains the host, mng_dn, mng_pw
public static Hashtable<String, String> getEnv( Map<String, String> configMap ) {
// Hashtable for environmental information
Hashtable<String, String> env = new Hashtable<String, String>();
// Specify which class to use for our JNDI Provider
env.put( Context.INITIAL_CONTEXT_FACTORY, INITCTX );
// Specify the host and port to use for directory service
env.put( Context.PROVIDER_URL, configMap.get( HOST ) );
// Security Information
env.put( Context.SECURITY_AUTHENTICATION, "simple" );
env.put( Context.SECURITY_PRINCIPAL, configMap.get( MGR_DN ) );
env.put( Context.SECURITY_CREDENTIALS, configMap.get( MGR_PW ) );
env.put( "java.naming.ldap.attributes.binary", "objectSID" );
return env;
}
I don't know if this was bad practice but to prevent the initialization from happening before every search I did an Init function that does :
if(Util.ctx == null ){
Util.init()
}
So the problem comes from here. My application will work roughly 30 mins (not sure of the time) and then the searches won't work anymore and I'll get the connection reset error in my console. My guess is that the connection is "closed" and it's not doing the initialization again since ctx is not null. I need help to figure out what to add to my if statement in order to prevent this error from happening. Maybe something like
if(Util.ctx == null || Util.ctx.isClosed() ){
Util.init();
}
I read on InitialDirContext and couldn't find what I need.
Don't try to keep reusing the same Context. Get a new one every time you need one. The server will close idle connections any time it feels like it, and isClosed() won't tell you when it has done so.
You can use the JNDI LDAP connection pooling feature to conserve connections.
What about set no timeout as follows:
// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Specify time-out to be infinite time . make it like never expired
env.put("com.sun.jndi.ldap.connect.timeout", "-1");
// Create initial context
DirContext ctx = new InitialDirContext(env);
And you can close it later when needed as follows:
finally {
if (obj instanceof Context) {
Context ctx = (Context) obj;
try {
ctx.close();
}
catch (Exception e) {
}
}
}
Check this out:
https://docs.oracle.com/javase/tutorial/jndi/newstuff/readtimeout.html
Related
I'm using google app engine JAVA 8 and servlet 3.1 and would like to use HikariCP for pooling.
I'll write my logic in pseudo-code for better understanding.
At this point when user connects to a servlet it creates a new connection to database every time.
so my servlet looks a bit like this
doGet(){
DatabaseObject db = new DatabaseObject()
Connection conn = db.getConnection()
db.createTable(conn)
db.readData(conn)
...
conn.close()
}
Now I've seen many pooling examples like this one
but first I'm not sure this is what I'm trying to achieve also I don't really understand the whole process
Any examples, explanations are welcome as I've tried searching the net and couldn't find some for servlets. So maybe I'm thinking the wrong direction
That example looks like it stores the pool in the app (servlet) context.
I've done it differently. Usually I create a class, call it MyDb. Then I add various methods to it to access data. Within it there is a getConnection() method.
Internally, MyDb has its own connection pool. getConnection() simply returns a connection from the pool. The pool is initialized when the first MyDb is created.
Something like this (this is for app engine so no port is specified):
private static DataSource pool = null;
public MyDb( String dbhost, String dbdsn, String dbuid, String dbpwd )
{
try
{
if( MyDb.pool == null )
{
String dbconn = null;
String dbclassname = null;
HikariConfig config = new HikariConfig();
dbconn = "jdbc:google:mysql://" + dbhost + "/" + dbdsn;
dbclassname = "com.mysql.jdbc.GoogleDriver";
config.setJdbcUrl( dbconn );
config.setUsername( dbuid );
config.setPassword( dbpwd );
MyDb.pool = new HikariDataSource( config );
}
catch( Exception e )
{
logger.error( e.getMessage() );
}
}
protected Connection getConnection() throws Exception
{
return pool.getConnection();
}
}
I've been trying to get a simple snippet of code to run in java - authentication as binder to Foxpass (hosted LDAP service) and then trying to authenticate another user to it.
I basically created a binder in my Foxpass settings and created some users as well.
The code is:
Hashtable<String,String> env = new Hashtable <String,String>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindUser);
env.put(Context.SECURITY_CREDENTIALS, bindPassword);
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(Context.PROVIDER_URL, myFoxpassUrl);
try {
DirContext ldapContext = new InitialDirContext(env);
NamingEnumeration<SearchResult> results = null;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setCountLimit(1);
controls.setTimeLimit(5000);
String searchString = "(&(objectCategory=user)(sAMAccountName=username))";
results = ldapContext.search("", searchString, controls);
System.out.println(results.hasMore());
}
catch (Exception e)
{
e.printStackTrace()
}
The binding (authentication with bindUsername and bindPassword works well). However, searching for the users return 0 values. I tried using:
sAMAccountName=myusername
sAMAccountName=myusername#mydomain.com
sAMAccountName=CN=myusername,DC=mydomain,DC=com
Or
username=myusername
...
Or
Email=myusername#mydomain.com
But nothing seems to work. Any help on finding the right format to do the search.
Thank you
It seems to me your searchString once lived in an Active Directory environment. With Foxbase try
String searchString = String.format("(uid=%s)", username);
I followed this tutorial to search in active directory.
Sample code :
class SearchSubtree {
public static void main(String[] args) {
Hashtable<String, Object> env = new Hashtable<String, Object>(11);
env
.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
try {
DirContext ctx = new InitialDirContext(env);
String[] attrIDs = { "sn", "telephonenumber", "golfhandicap", "mail" };
SearchControls ctls = new SearchControls();
ctls.setReturningAttributes(attrIDs);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(sn=Geisel)(mail=*))";
NamingEnumeration answer = ctx.search("", filter, ctls);
// Print the answer
ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
But NameNotFoundException is thrown at
NamingEnumeration answer = ctx.search("", filter, ctls);
But when I pass, "DC=extldap,DC=com" as first argument, code works fine.
Is there any issue with the tutorial? Can first argument not be empty string? Or is this a limitation with Active Directory?
Generally speaking, for LDAP servers you always need a root context to start your search from. Basically, you are doing the equivalent of trying to search a SQL database without specifying a database or table name.
Some server implementations may allow an empty context (I know iPlanet used to allow it in some cases) but these are exceptions to the rule.
The javadoc for DirContext.search() says:
Searches in the named context or object for entries that satisfy the
given search filter. Performs the search as specified by the search
controls.
See search(Name, String, SearchControls) for details.
Parameters:
name the name of the context or object to search
...
Usually, in Active Directory it is fine to start searching from the domain root, which is always DC=<your>,DC=<domain>.
That is why your second search works.
I have been stuck at a point for so long. I am trying to authenticate a user from an LDAP directory using LDAP via JNDI, but the code attached below returns nothing for "results" i.e. the search method (marked with **) returns null. I tried to debug the code, but did not understand what exactly is going on behind the "search" function of "DirContext" class. Can anybody help please?
public class Authenticate {
#SuppressWarnings({ "unchecked", "rawtypes", "unused" })
public static void main(String[] args) throws NamingException {
final String ldapAdServer = "ldap://iauth.tum.de:389/cn=someuser,ou=users,ou=data,ou=prod,ou=iauth,dc=tum,dc=de";
final String ldapUsername = "cn=someuser,ou=users,ou=data,ou=prod,ou=iauth,dc=tum,dc=de";
final String ldapPassword = "somepassword";
Hashtable env = new Hashtable();
env.put(Context.SECURITY_AUTHENTICATION, "none");
if(ldapUsername != null) {
env.put(Context.SECURITY_PRINCIPAL, ldapUsername);
}
if(ldapPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, ldapPassword);
}
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapAdServer);
DirContext ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> results = null;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
**results = ctx.search("", "(objectClass=Person)", controls);**
while (results.hasMoreElements())
{
SearchResult searchResult = (SearchResult) results.next();
Attributes attributes = searchResult.getAttributes();
Attribute attr = attributes.get("cn");
}
}
}
Since the search method of "DirContext" (marked in **), returns nothing, I can never enter the while loop. Can anybody tell where exactly I am being wrong.
You appear to be searching an Active Directory instance. If that's the case (and even if it's not the issue may be the same), then the problem is not to do with your search code but with the initial setup. In particular, this line is at fault:
env.put(Context.SECURITY_AUTHENTICATION, "none");
Per the documentation, setting SECURITY_AUTHENTICATION to "none" performs an anonymous bind, meaning that to the LDAP directory your search request appears to be coming from an anonymous user. By default, Active Directory does not return an error when it gets an anonymous bind request, but the anonymous user does not normally have permissions to search the directory. To ensure that your context is created and bound to the appropriate identity, you should change the line in question to:
env.put(Context.SECURITY_AUTHENTICATION, "simple");
Or even not set the SECURITY_AUTHENTICATION parameter at all, since it defaults to "simple".
Change your filter to "(objectClass=*)" and see, otoh, you are using JNDI not Apache LDAP API like you mentioned in the question.
I am trying to place an object in JNDI, so that only one of the progam should be able to place it in JNDI. is there any global lock that can be used in J2EE environment. Is RMI can be used for this purpose? please provide any reference links. Thanks in advance.
Also, what is NameAlreadyBoundexception? I am trying to use it as a method to synchronize, i.e, only one program places it in JNDI and if other trying to bind should get that exception.
But when i am testing the multiple binding I am not getting the Exception.And second binding is done. look up is giving the second object bound. here is my code:
public class TestJNDI {
private static String JNDI_NAME = "java:comp/env/test/something";
public static void main(String[] args) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,"t3://127.0.0.1:7001");
Context ctx = new InitialContext(env);
System.out.println("Initial Context created");
String obj1 = "obj1";
String obj2 = "obj2";
try{
ctx.bind(JNDI_NAME, obj1);
System.out.println("Bind Sucess");
}catch(NameAlreadyBoundException ne ){
// already bound
System.out.println("Name already bound");
}
ctx.close();
Context ctx2 = new InitialContext(env);
try{
// Second binding to the same name not giving the Exception??
ctx2.bind(JNDI_NAME, obj2);
System.out.println("Re Bind Sucess");
}catch(NameAlreadyBoundException ne ){
// already bound
System.out.println("Name already bound");
}
String lookedUp = (String) ctx2.lookup(JNDI_NAME);
System.out.println("LookedUp Object"+lookedUp);
ctx2.close();
}
}
When you close the first content ctx1 you release any objects bound to it see : Context
So your second context has nothing to do with the first one.