Liferay 6.1 - connecting to LDAP server

I've been working on a portal project recently using Liferay.  After getting Liferay installed on a local VM I was tasked with connecting it to an LDAP server for user authentication and importing groups for permissions.

Adding an LDAP server is simple.  Liferay has buttons for testing the connection, user import (with preview) and group import (with preview) that are extremely helpful.  Obviously, all of your settings will depend on the LDAP server you're connecting to.  In my case I didn't need to change any of the user and group default settings.  I did have to change the defaults for the connection.

I  found that using the Liferay control panel was the best way to connect to the LDAP server and determine the settings you need.  What you do with the settings after that really depends on your situation.  I'm a developer, and the project required extensive Liferay customizations.  Therefore, I moved the settings into an Ext plugin which we managed in SVN and deployed to multiple environments.  Alternatively, each environment can be manually however you may find this to be difficult to manage.
I wanted my own LDAP server because I needed to create my own set of users, groups, and have complete control over the structure.
I installed openLDAP locally and connected to it using jxplorer.  Then I created a bunch of groups and users by importing an ldif file.  My database looked good, and connecting/navigating it using jxplorer worked fine.  
To help verify/debug connecting to LDAP servers I used jxplorer, which is free and available for all of the common OSs.
The Technical Details

In my case the LDAP server is a test server, and was really easy to connect.  The hardest part was finding where the LDAP settings are located in the Liferay control panel. To get to the LDAP settings:
  1. Login to Liferay (http://localhost:8080, in my case test@liferay.com:test)
  2. Navigate to the control panel (Go To->Control Panel)
  3. Then under Portal in the left navigation, go to Authentication->LDAP

The options for LDAP are pretty self-explanatory:
  • Enabled - enable LDAP authentication
  • Required - require LDAP authentication (the only exception I'm aware of is the default admin user created when first logging into Liferay)
  • Import Enabled - import LDAP users and groups into Liferay
  • Export Enabled - export user and group changes made in Liferay to LDAP
  • Use LDAP Password Policy - I recommend selecting this, otherwise when users first login they will be forced to change their password
There are some useful LDAP options (for Liferay 6.1) that you can add to your portal-ext.properties file as well.  In my case I'm using the glassfish hosted version of Liferay 6.1, so my portal-ext.properties file is in liferay-portal-6.1.20-ee-ga2/glassfish-3.1.2/domains/domain1/applications/liferay-portal/WEB-INF/classes.  The following are some of the more useful settings that I used.
  • ldap.import.on.startup=false
  • ldap.import.interval=10
  • ldap.import.create.role.per.group = true
    • This is to create a role for each group, and add each group member to that role
    • I use this because all our permissions are role based
  • ldap.import.enabled=true
  • ldap.export.enabled=true
  • ldap.import.method=group
    • Only import users that are part of groups
    • The other option is user, i.e. only import groups that have users

In my case I needed to fill out my Base DN to import the users (dc=example,dc=com).
  •  Connection
    • Base Provider URL - ldap://
    • Base DN - dc=example,dc=com
    • Principal - cn=Manager,dc=example,dc=com
    • Credentials - *****
  • Users
    • All defaults
  • Groups
    • All defaults
  • Export
    • Users DN - dc=example,dc=com
    • User Default Object Classes - top,inetOrgPerson,person
    • Groups DN - dc=example,dc=com
    • Group Default Object Classes - top,groupOfUniqueNames

Minor Issues I Encountered

The first issue you might run into is a certificate problem (if using ldaps).  Logging in will simply fail, and there won't be anything in the logs either.  To fix the problem you have to add the LDAP certificate to the Java cacerts store.

Then, Liferay could only connect.  Trying to import users and groups failed.  Eventually I determined that the problem was because I hadn't added the user I use to login to LDAP to the LDAP database.  I'm not sure why Liferay requires this and jxplorer doesn't, or for that matter why the user isn't automatically added to the database during installation/setup, but it's not.

During the import I ran into an problem.  I had issues (debugged continually restarting Liferay and using the liferay log file which was very time consuming) with screen names.  I simplified all my users so that they only contain alpha-numeric characters for all properties (cn, sn, uid, etc...).

Another possible issue you may run into is how LDAP is storing passwords.  By default Liferay seems to expect plain text, so that's what I'm using on my LDAP server.  Obviously you don't want to do this in production.  However, during development plain text is fine.

LDIF Snippet for Generating Users

Here's a snippet of the LDIF file I imported to setup my users and groups (LDIF->Import file in jxplorer):

dn: cn=u1,ou=users,dc=example,dc=com
cn: u1
sn: u1
objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
uid: u1
ou: users
givenName: u1
title: Mr.
mail: u1@
userpassword: u1

dn: cn=u2,ou=users,dc=example,dc=com
cn: u2
sn: u2
objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
uid: u2
ou: users
givenName: u2
title: Mr.
mail: u2@
userpassword: u2

dn: cn=u3,ou=users,dc=example,dc=com
cn: u3
sn: u3
objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
uid: u3
ou: users
givenName: u3
title: Mr.
mail: u3@liferay.com
userpassword: u3

dn: cn=g1,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: g1
description: g1
uniquemember: cn=u1,ou=users,dc=example,dc=com
ou: groups

dn: cn=g2,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: g2
description: g2
uniquemember: cn=u2,ou=users,dc=example,dc=com
ou: groups

dn: cn=g3,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: g3
description: g3
uniquemember: cn=u3,ou=users,dc=example,dc=com
ou: groups


Giving an old wireless router (WRT54G) a new life

Over the holiday break, I had some spare time to work on projects around the house.  One of the projects was to give my old wireless router (WRT54G) a new life as a repeater.

I can't say enough about my current wireless router, an Almond+ by Securifi, it works great, it's easy to use and has tons of automation features.  However, it's located at the front of my house and coverage isn't great at the back of my yard.  To solve my 1st world problem, I reconfigured my old WRT54G.

Here's a couple of things to keep in mind during the process:

  • It's good to have separate internet access throughout the process so that if you have to look anything up, you can find a solution quickly.  My smartphone did the trick for me.
  • Be patient, after restarting the WRT54G sometimes the web interface (LuCi) and SSH access were slow to come up (connection lost message, etc...).  Wait a minute and try again
To start, I dusted off my WRT54G and got to work.  I checked out openwrt, and found my router in the OpenWRT Table of Hardware (make sure you pay attention to version numbers listed on the bottom of your router) and read the WRT54G device page.  It's a good idea to read the entire page, but really the most important section is Installing OpenWRT

I installed backfire v10.03.1 brcm-2.4 on my v3 router.  It was simple to follow their steps, and I had no problems at all.

Next, because I wanted to extend my existing WLAN, I configured the router to be a wireless client bridge (bridgedclient mode).  You can find the recipe here.

The most complicated part of the process was configuring the router for my WLAN.  Here are a couple of notes to help:
  • You'll need a telnet client and/or SSH client installed on the computer you're using to access the router and configure it
  • The router is running Linux, so I used vi (vi cheat sheet) to edit the files, which is a text editor and is a bit confusing if you're not familiar with it
  • All the configuration files are in /etc/config/
  • The default IP address of the WRT54G is in /etc/config/network, and was set to so I had to first change my laptop's IP to the same network so that I could access the router (via a wired Ethernet connection).
  • To change my laptop's IP:
    • Disable DHCP (control panel -> network connections -> lan connection properties -> TCP/IP v4 properties, set the IP to and default gateway to
  • Make sure re-enable DHCP on your laptop so that you can access the WRT54G after it is restarted

So that I don't forget the static IP of my WRT54G, I wrote it on the bottom of the device.  Then I plugged it in near the back of my house, and now I have extended WLAN coverage.


Why is the JVM on my Linux Server using so much CPU?

Once in a while the CPU usage on one of our development servers rails and hits 100% and never lets up.  A couple of times it actually hit 200% (2 threads burning 2 cores at 100%) or more before we noticed.  These are not good situations, and something you want to get to the bottom of and fix before it starts happening in your production environment.
The lazy thing to do is restart the entire server, or restart the process that's railed.  But this is just ignoring the problem.  Avoidance is what you should be doing.  Here's how you get to the bottom of what's going on:
1. Get the PID causing the problem, run top to get the list of processes using the most CPU, it'll be the first entry.  As you can probably guess, it was my JVM.
2. Get the IDs of the threads in the process causing the problem (my PID was 2089): top -H -p 2089
11405 liferay   20   0 11.7g 6.8g  14m R 99.4 88.5  23074:07 java
10000 liferay   20   0 11.7g 6.8g  14m R 99.4 88.5  23079:00 java
 3390 liferay   20   0 11.7g 6.8g  14m S  3.9 88.5  22:42.06 java
 2089 liferay   20   0 11.7g 6.8g  14m S  0.0 88.5   0:00.02 java
 2098 liferay   20   0 11.7g 6.8g  14m S  0.0 88.5   0:01.85 java
 2111 liferay   20   0 11.7g 6.8g  14m S  0.0 88.5   3:15.03 java
 2112 liferay   20   0 11.7g 6.8g  14m S  0.0 88.5   3:11.32 java

From the top listing, it's easy to see the first two threads are the problem, 11405 and 10000 are using almost 100% of the CPU each.  Convert the thread IDs to hex for step 4.
11405 -> 0x2C8D
10000 -> 0x2710

3. Take a thread dump of your JVM: /usr/java/jdk1.6.10/bin/jstack 2089 > /tmp/td.log
4. Search your thread dump for the threads causing your problem (found in step 2).  For example, I found the following:
"pool-400-thread-53" prio=10 tid=0x00007fde4801efd0 nid=0x2c8d runnable [0x00007fdde3ba4000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap.getEntry(HashMap.java:347)
        at java.util.HashMap.containsKey(HashMap.java:335)
        at java.util.HashSet.contains(HashSet.java:184)
        at com.vaadin.ui.CustomTable.unregisterPropertiesAndComponents(CustomTable.java:2322)
        at com.vaadin.ui.CustomTable.getVisibleCellsNoCache(CustomTable.java:2219)
Looking through the stack trace for 0x2c8d, I was eventually able to find a custom class and a line number that allowed me to narrow down the source of the problem.
Finding the general area of the problem is usually the easy part.  The hard part is eliminating the source of the problem, which I'll leave up to you.
Good luck.