Showing posts with label jndi. Show all posts
Showing posts with label jndi. Show all posts

Thursday, May 20, 2010

Accessing MS Active Directory using LDAP

In this example, we'll implement a simple client that performs two simple lookups in Microsoft Active Directory to retrieve groups and users through the LDAP interface. The LDAP interface can be accessed using the standard JNDI API.

The Microsoft implementation of the LDAP interface is limited to 1000 query results. So to handle more than 1000 results, we have to maintain a cookie that stores the current position of retrieved results and pass the cookie back to the server to continue the query from the cookie stored position. This is called a paged query.

We first start by creating a LdapContext object, which we'll use to access Active Directory through the LDAP interface. The Context object is configured using a Hashtable.

Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "<user>");
env.put(Context.SECURITY_CREDENTIALS, "<password>");
env.put(Context.PROVIDER_URL, "ldap://<host>:389");
env.put(Context.REFERRAL, "follow");

// We want to use a connection pool to
// improve connection reuse and performance.
env.put("com.sun.jndi.ldap.connect.pool", "true");

// The maximum time in milliseconds we are going
// to wait for a pooled connection.
env.put("com.sun.jndi.ldap.connect.timeout", "300000");

Next, we need to specify how we are going to search the directory with a SearchControls object, from where we want to start the search with a search base string, and how we want to filter results.

SearchControls searchCtls = new SearchControls();
// We start our search from the search base.
// Be careful! The string needs to be escaped!
String searchBase = "dc=company,dc=com";

// There are different search scopes:
// - OBJECT_SCOPE, to search the named object
// - ONELEVEL_SCOPE, to search in only one level of the tree
// - SUBTREE_SCOPE, to search the entire subtree
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

// We only want groups, so we filter the search for
// objectClass=group. We use wildcards to find all groups with the
// string "<group>" in its name. If we want to find users, we can
// use: "(&(objectClass=person)(cn=*<username>*))".
String searchFilter = "(&(objectClass=group)(cn=*<group>*))";

// We want all results.
searchCtls.setCountLimit(0);

// We want to wait to get all results.
searchCtls.setTimeLimit(0);

// Active Directory limits our results, so we need multiple
// requests to retrieve all results. The cookie is used to
// save the current position.
byte[] cookie = null;

// We want 500 results per request.
ctx.setRequestControls(
new Control[] {
new PagedResultsControl(500, Control.CRITICAL)
});

// We only want to retrieve the "distinguishedName" attribute.
// You can specify other attributes/properties if you want here.
String returnedAtts[] = { "distinguishedName" };
searchCtls.setReturningAttributes(returnedAtts);

// The request loop starts here.
do {
// Start the search with our configuration.
NamingEnumeration<SearchResult> answer = ctx.search(
searchBase, searchFilter, searchCtls);

// Loop through the search results.
while (answer.hasMoreElements()) {
SearchResult sr = answer.next();
Attributes attr = sr.getAttributes();
Attribute a = attr.get("distinguishedName");
// Print our wanted attribute value.
System.out.println((String) a.get());
}

// Find the cookie in our response and save it.
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof
PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) controls[i];
cookie = prrc.getCookie();
}
}
}

// Use the cookie to configure our new request
// to start from the saved position in the cookie.
ctx.setRequestControls(new Control[] {
new PagedResultsControl(500,
cookie,
Control.CRITICAL) });
} while (cookie != null);

// We are done, so close the Context object.
ctx.close();

We can also print all attributes from an object using its fully distinguished name using the following code:

String dN = "CN=JohnDoe,OU=Users,DC=company,DC=com";
Attributes answer = ctx.getAttributes(dN);

for (NamingEnumeration<?> ae = answer.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
String attributeName = attr.getID();
for (NamingEnumeration<?> e = attr.getAll(); e.hasMore();) {
System.out.println(attributeName + "=" + e.next());
}
}

Tuesday, April 13, 2010

Using JavaMail API with Glassfish and GMail

In this post, I'll show you how to configure JavaMail to use the SMTP server of GMail in Glassfish. This way, you avoid hardcoding server addresses in your application and make your application more portable. It also minimalizes the amount of "plumping" code.

Setup Glassfish

First, fire up the admin screen of Glassfish and click on JavaMail Sessions. Then create a new session configuration by clicking "new".

Make sure the following fields are filled in:

  • JNDI name: mail/<name>
  • Mail Host: smtp.gmail.com
  • Default User: <email>
  • Default Return Address: <email>

The JNDI name must be prefixed with "mail/". Use your GMail email address in last two fields.

Skip the Advanced settings and create additional properties at the bottom of the screen:

  • mail.smtp.socketFactory.port: 465
  • mail.smtp.port: 465
  • mail.smtp.socketFactory.fallback: false
  • mail.smtp.auth: true
  • mail.smtp.password: <gmail_password>
  • mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory

Use your valid GMail password in the password field. Save, and your configuration is ready for use.

Example client to send email with attachments

Next, we create a client to use the newly created JavaMail service in Glassfish. Before we can do that, we need the application server libraries: appserv-rt.jar and javaee.jar. You can find them in the lib directory of your Glassfish installation. Make sure all the libraries are in your build/class-path.

If you still get unresolved classes related to JavaMail, then you might also need the JavaMail libraries, which you can download here.

The example client below builds an email with three attachments from three different sources, and sends the email to two recipients. You have to use MIME-types to indicate the type of attachments. Save the code below as Main.java, compile, and run!

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.util.ByteArrayDataSource;
import javax.naming.InitialContext;

public class Main {

public void runTest() throws Exception {
InitialContext ctx = new InitialContext();
Session session =
(Session) ctx.lookup("mail/<name>");
// Or by injection.
//@Resource(name = "mail/<name>")
//private Session session;

// Create email and headers.
Message msg = new MimeMessage(session);
msg.setSubject("My Subject");
msg.setRecipient(RecipientType.TO,
new InternetAddress(
"tony@email.com",
"Tony"));
msg.setRecipient(RecipientType.CC,
new InternetAddress(
"michelle@email.com",
"Michelle"));
msg.setFrom(new InternetAddress(
"jack@email.com",
"Jack"));

// Body text.
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText("Here are the files.");

// Multipart message.
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);

// Attachment file from string.
messageBodyPart = new MimeBodyPart();
messageBodyPart.setFileName("README1.txt");
messageBodyPart.setContent(new String(
"file 1 content"),
"text/plain");
multipart.addBodyPart(messageBodyPart);

// Attachment file from file.
messageBodyPart = new MimeBodyPart();
messageBodyPart.setFileName("README2.txt");
DataSource src = new FileDataSource("file.txt");
messageBodyPart.setDataHandler(new DataHandler(src));
multipart.addBodyPart(messageBodyPart);

// Attachment file from byte array.
messageBodyPart = new MimeBodyPart();
messageBodyPart.setFileName("README3.txt");
src = new ByteArrayDataSource(
"file 3 content".getBytes(),
"text/plain");
messageBodyPart.setDataHandler(new DataHandler(src));
multipart.addBodyPart(messageBodyPart);

// Add multipart message to email.
msg.setContent(multipart);

// Send email.
Transport.send(msg);
}

public static void main(String[] args) {
Main cli = new Main();
try {
cli.runTest();
} catch (Exception e) {
e.printStackTrace();
}
}
}