Tuesday, August 7, 2012

Including External jars in your Liferay Extension

This is one that I've seen asked about a lot online, and the solution is so painfully simple that I can only conclude that the people who know the answer aren't bothering to help out those who are asking.

That's what this blog is for.

The environment:

Liferay 6.0
Apache Ant

You're trying to use the ant deploy command either from within Eclipse or on the command line but you have a dependency on another .jar file you need to include in the compilation.  If you do this in Eclipse, and include the .jar file in your project build path in Eclipse, the IDE will add a line to your any .classpath file pointing to this dependency.

The problem is that doesn't help your ant find the file.

If you need to include external .jar resources in your Ext, just drop a copy of the .jar you need in your Project/docroot/ext-lib/global folder.  Your ant deploy target should now be able to use it fine.

Thursday, April 26, 2012

Phase Listener Not Running

This is another one of those times when becoming complacent with IDE tools will turn around and bite you.


The environment:

IDE: Eclipse Helios SR2
Portal: Liferay 6.0.6
Development: JSF 2.0 (Mojarra)/ICEfaces 2.0

The Phase Listener I included in my portlet absolutely did nothing.  No errors... No crashes... But it simply didn't respond in any way, shape or form.

Yes, it was configured in the faces-config.xml.

Yes, it implemented PhaseListener.

The problem turned out to be that I had allowed Eclipse to automatically add the unimplemented methods to the class when I was creating my PhaseListener.  The problem was in this method:

public PhaseId getPhaseId() {
// TODO Auto-generated method stub
return null;

See the problem?

It should have said

public PhaseId getPhaseId() {
// TODO Auto-generated method stub
return PhaseId.ANY_PHASE;

I hadn't paid any attention to the method after it was auto-generated because I didn't need to modify it, so I didn't notice the problem.

It's a nice feature in Eclipse, but always verify that when it does things to save you some time, that it does them correctly.

Thursday, March 22, 2012

RichFaces Error: filterStart

RichFaces has recently moved into version 4, providing compatibility with JSF 2.0.  Pretty cool.

The problem is that it can be a bit confusing when upgrading from version 3, or when following some of the slightly outdated documentation on the site.

If you've dutifully followed the getting started instructions for RichFaces when working with version 4, you probably added these lines to your web.xml file:

     <display-name>RichFaces Filter</display-name>
     <servlet-name>Faces Servlet</servlet-name>

And yet you're still getting that horrible filterStart error, aren't you?  No problem, my friends.  Here's how to fix it:

Delete those lines from your web.xml.


The JSF 2.0 spec handles those configurations already.  By adding those lines into your web.xml you're just going to confuse the poor thing.

SEVERE: Error listenerStart Part IV

By now you've gone through the other possible solutions on this site to get your JSF Portlet to deploy and are still seeing this evil,  horrible, unholy error message and you're seriously contemplating changing careers...

It hit me this afternoon that there is yet another possible cause for this error that might catch the new developer off guard.

Which version of JSF are you using?

No, I don't mean the version number.  I mean which implementation?  Don't know?  Better find out, because there are two very popular ones.

The first is Mojarra, which is the JSF implementation developed by Sun.  It's the one I've used most often in my own JSF Portlets in Liferay.  The other is MyFaces from Apache.  They both comply with the JSF standard, and are usually interchangeable...


You have to make sure you know which one you're dealing with when you're editing your web.xml.  When you configure your listener, you need to know which implementation you're working with so that you can configure it to call the correct class.

If you're using Mojarra, then your listener should look like this:


If you're using Apache MyFaces, then it should look like this:

<listener> <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class> </listener>

Make sure you're configuring for the right implementation!

Tuesday, March 6, 2012

CILogon and Liferay Part 6: Signing Out

This is part 6 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

So by now you've got your Liferay signing you in using your CILogon provider and all is well.  Your boss is pleased, and says "Can I try?"  Of course, you say, and slide the demo laptop over to him.  He clicks the "Sign Out" link in the upper right corner of the page and...

...you're still signed in.  He slides it back over to you, smile fading as you start clicking that link over and over, but Liferay's got you in an iron grip and ain't letting go.

Time to go back to your extension.

See, Liferay has a Struts driven LogoutAction object that clears the session and kills all the Liferay cookies... But it's not killing your CILogon cookies because, well, you made them, not Liferay. We need to modify that class to include the cookies your CILogonAutoLogin module keeps using to log you back in.

Grab the following file from the Liferay source code: com.liferay.portal.action.LopgoutAction.java and put it into your ext plugin in the corresponding folder.

About halfway down the file is a series of lines of code adding cookie objects to CookieKeys.  Right before those lines is a good place to add this code:

Cookie cILogonEmailCookie = new Cookie(

if (Validator.isNotNull(domain)) {


Cookie cILogonNameCookie = new Cookie(

if (Validator.isNotNull(domain)) {


CookieKeys.addCookie(request, response, cILogonNameCookie, false);
CookieKeys.addCookie(request, response, cILogonEmailCookie, false);

What's happening here is that we're replacing the cookies we created in SuccessServlet with new ones that will expire immediately. That way when the user comes back around in the request the CILogonAutoLogin module won't see the cookies and log the user back in.

Here's an important note... We're using the domain String object from the line

String domain = CookieKeys.getDomain(request);

earlier in the class.  This is not the full string you may expect.  For example, if this code is running on machine.mydomain.com, the domain that's returned here will be ".mydomain.com" so when you're setting this string in your SuccessServlet (back in part 2 of this series we called that variable TARGET_DOMAIN) make sure it matches!

I'm serious.  It took me a day and a half to figure out why Liferay was able to find the cookie, and yet wasn't overwriting the old cookie with the new one.  For this to work, the cookie domains must match exactly!

One quick note about setting that cookie back in SuccessServlet... I didn't go into specific detail but remember we NEVER hard code values!  If your domain is hard coded in that servlet for testing purposes fine... But as soon as possible move that string value into a config file or database entry or something.  Don't leave it in the compiled code.

This project incorporates tools whose development was funded in part by the NIH through the NHLBI grant: The Cardiovascular Research Grid (R24HL085343)

Thursday, March 1, 2012

CILogon and Liferay Part 5: The Liferay Extension

This is part 5 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

And now we build the Liferay Extension!

If you've never done so, you should review the steps here.

So you have your Liferay Extension now in your Eclipse and are ready to roll!  In the Shibboleth steps I described a way to configure Shibboleth inside the Liferay Portal using a hook plugin.  Well, in this project we're going to roll that into the Extension, so there will be no need for a separate hook.

Now, create a folder structure to match Liferay in your ext project. We're going after the WEB-INF/ext-impl/src/com/liferay/portal/security/auth folder. That'll be in the docroot folder of your project. Now, if you look at the corresponding folder in your Liferay source code you'll notice a series of auto login classes corresponding to the various Web SSO providers Liferay is designed to interact with out of the box. We're going to build one just like them for CILogon.

Your new CILogonAutoLogin class should be of package com.liferay.portal.security.auth and it should implement AutoLogin.

Here's the code:

public class CILogonAutoLogin implements AutoLogin {

private static Log _log = LogFactoryUtil.getLog(CILogonAutoLogin.class);
private long companyId; 

public String[] login(HttpServletRequest req, HttpServletResponse res)
throws AutoLoginException {

String[] credentials = null;
String userEmail = "";
String[] userName;
User user;

_log.info("CILogon Extension");

try {
companyId = PortalUtil.getCompanyId(req);

if (!PrefsPropsUtil.getBoolean(
companyId, PropsKeys.CILOGON_AUTH_ENABLED,
System.out.println("CILogon Enabled.");
return credentials;

userEmail = getEmailFromCookie(req.getCookies());

if (userEmail.equals("") || userEmail.length() < 1) {
_log.error("Invalid or missing user login information from CILogon");
return credentials;

credentials = new String[3];

user = UserLocalServiceUtil.getUserByEmailAddress(PortalUtil.getCompany(req).getCompanyId(), userEmail);
}catch (NoSuchUserException e) {
_log.error("No user found to match " + userEmail);

credentials[0] = String.valueOf(user.getUserId());
credentials[1] = user.getPassword();
credentials[2] = Boolean.TRUE.toString();

} catch (Exception e) {
throw new AutoLoginException(e);

return credentials;

private String getEmailFromCookie(Cookie[] cookies){

String email = "";

if(cookies != null){
for (Cookie ck : cookies) {
if ("CILOGON-USER_EMAIL".equals(ck.getName()) && !ck.getValue().equals("")) {
_log.info("User Login received:" + ck.getValue());
email = ck.getValue();

return email;


This should look familiar if you read my post on creating the extension for Shibboleth.  It's very similar, only it gets the user data from a cookie instead of an attribute.  Note the name of the cookie is the same as what we set in the SuccessServlet.

 Be sure to enable this module in your portal-ext.properties file:

#CILogon Auto Login Setup
auto.login.hooks=com.liferay.portal.security.auth.CILogonAutoLogin, com.liferay.portal.security.auth.RememberMeAutoLogin

In your Ext project, add a new set of folders under docroot/WEB-INF/ext-impl/src:
And copy the PropsKeys.java class from the corresponding location in the Liferay source bundle into util.

What you'll see in this class is a VERY LONG list of public Strings being declared. Add these into it:

public static final String CILogon_AUTH_ENABLED = "cilogon.auth.enabled";

It doesn't matter where you put it, but since the variables are in alphabetical order I just kept to that approach.

Now you can save that class and add another folder set:

also under /src.

From the corresponding folder in the source code, copy over the PropsValues.java file.
You'll see another long list of Strings, similar to PropsKeys. Add these lines:

public static final boolean CILOGON_AUTH_ENABLED = GetterUtil.getBoolean(PropsUtil.get(PropsKeys.CILOGON_AUTH_ENABLED));

And save the file.

Now, let's modify the Admin UI to enable or disable our CILogon module.  The path here will be similar to the Shibboleth hook path we did in the other post, only now it's in our extension:


From the corresponding folders in your Liferay source, copy the file authentication.jsp over.

What you'll see when you open this file is the code that sets up the Authentication settings in Portal Settings. At the top is yet another long series of variable declarations. Add this to the list:

boolean cilogonAuthEnabled = ParamUtil.getBoolean(request, "settings--" + PropsKeys.CILOGON_AUTH_ENABLED + "--", PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys. CILOGON _AUTH_ENABLED, PropsValues. CILOGON _AUTH_ENABLED));

This declaration is referencing our friends, the value pair in Liferay. At last we'll be writing code to set them.
Near the top of the HTML you'll see a <liferay-ui> tag with an attribute called "names." That attribute sets up the list of tab links to choose from in the menu. You should see the names of all the existing Web SSO providers. Just add "CILogon" to the end of the list.
Next, we see a series of <liferay-ui:section> tags. Each of these sections corresponds to one of the Web SSO providers. Add a new section to the end that looks like this:

   < aui:fieldset>
    < aui:input inlineLabel="left" label="enabled" name=' < %= "settings--" + PropsKeys.CILOGON_AUTH_ENABLED + "--" %>' type="checkbox" value=" < %= cilogonAuthEnabled %>" />

And save the file. Now you can deploy your new extension and start Liferay!

When you go to set these values in the CILogon configuration in Liferay, they're pretty self explanatory. Just  make sure the "Enabled" box is checked. Save the value.

This is what mine looks like.  Yes, the Shibboleth module is still in there too!

Last, it's nice to get logging from this module so from in Liferay, logged in as an Admin, go to the Control Panel and then go into "Server Administration." Click "Log Levels."

Click "Add Category."

In the box, add com.liferay.portal.security.auth.CILogonAutoLogin and click "save."

I'll put this project up on the Github account once it's been refined a bit more, but you see the basics.
This project incorporates tools whose development was funded in part by the NIH through the NHLBI grant: The Cardiovascular Research Grid (R24HL085343)

CILogon and Liferay Part 4: The Liferay Theme

This is part 4 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

Technically, this step is optional.  What we're doing here is changing the Sign In link in the upper right corner of the Liferay page to point to your CILogon servlets.  If you intend to come at the portal from a different site then you can skip this step, although it would still be good to have this link either changed or disabled so that it's consistent with your logon approach.

First, you need to create a Liferay Theme plugin.  If your Liferay SDK isn't already set up, take a few minutes and follow the procedure here.

It's a LOT easier to modify an existing theme than to build one from scratch.  If you already have a  theme you're using, modify that.  If you don't, you can import the default Liferay theme and use it.  (It can be found in [liferay-portal-home]/[tomcat-home]/webapps/ROOT/html/themes/classic).  If you have the Liferay plugin for Eclipse, it can handle the details for you.

If you need to do it by hand, you can run the following ant command in the themes folder of your SDK:

create myAwesomeTheme "My Awesome Theme"

Ant will create an empty theme project for you.  Then, you can copy the files from the /classic themes folder described above into the /docroot folder of your theme project.  Do NOT delete the /_diffs folder!

The way we make changes to a them in Liferay is to override the files, not edit them directly.  This helps you to keep your changes straight and easy to keep track of.

In the _diffs folder, create a folder called templates.  Now, from the templates folder under docroot, copy the portal_normal.vm and init_custom.vm files.

Open init_custom.vm.  It should be blank.  Add the following line:

#set( $custom_sign_in_url = "[MyAwesomeDomain]/portal/startRequest" )

Save it and close it.

Now, in portal_normal.vm, find the line

<a href="$sign_in_url" id="sign-in" rel="nofollow">$sign_in_text</a>

And change it to

<a href="$custom_sign_in_url" id="sign-in" rel="nofollow">$sign_in_text</a>

So it uses your custom velocity variable that points to the CILogon servlets.

Now you can deploy your theme to Liferay and apply it!

CILogon and Liferay Part 3: Installing the Servlets

This is part 3 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

So your Servlets build successfully in Maven, right?  if not, go back and make sure they do before proceeding. Also, by now you should have either your official X509 certificate form your CA or gotten a test cert from CILogon, along with instructions on how to install it.  If not, get these before going any further.

Let's configure Tomcat:

Open up the server.xml file in Tomcat's conf folder.  Find the tag for the secure Connector port.  It may be commented out by default.  Uncomment it and edit it to look like this:

    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
keystoreFile="[The path to your local keystore]"
keystorePass="[Your keystore password]" />

You should have the path and password for your keystore from the installation steps when you received the certificate.  This isn't an optional step, even for testing.  The CILogon establishes a trust before sending you to your IdP, and if it can't do that you'll go no further.

All done?  Good.  Now restart your Tomcat.

Once Tomcat is back up and running, and there are no errors related to your keystore, you can install your CILogon servlet app.  Take the war file and copy it to the /webapps folder in your Tomcat.  It will automatically hot deploy.  Watch the catalina.out file and make sure no errors arise from the installation.

Done?  Good.  On to the next step...

Wednesday, February 29, 2012

CILogon and Liferay Part 2: The CILogon Servlets

This is part 2 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

If you're reading this, then by now you have your Liferay Portal up and running, you have the CILogon portal servlet project in your Eclipse, you have a cert ready to go, and you're feeling either nervous or pleased with yourself, maybe both...

Now we dive into some servlet code.

The CILogon servlet app was originally written by Jeff Gaynor, a highly intelligent and amazingly patient fellow who created this app for use as a starting point for people like you and me who want to use it as a platform for portal authenticating awesomeness.  He designed it to be pretty flexible, so the approach I took is only one of many, and you may find that once you get into this and understand how it works better, a different approach may work better for you.  Remember what I said in the last post about leaving the elegance to the reader?  Well, I meant it!

Within the project you'll find a set of files under src/main/resources which are templates for the configuration you're going to use.  Which one you use is up to you, and will depend on the exact requirements and architecture of your system.  I went with memory.xml, but there's also file.xml and postgres.xml.

The important thing to focus on in the file is the hasPortalParameters section.  Here you will configure the URIs for the servlets in this application.  All you really need to change is the domain name for your server.  The callbackUri element will point to the ready servlet (which is in a jar that Maven got from the repository) and the failureUri element points to the failure servlet, which, shockingly, is defined by the FailureServlet class.  The portalName tag is whatever you named you rportal, and the successUri points to the...  you guessed it... success servlet (SuccessServlet).

So when you're done configuring this thing, it'll look something like this:

             < rdf:Description rdf:about="ncsa:cilogon.org,2010:206d23fd4830174408e16c649f6d4a06">
                 < rdf:type rdf:resource="ncsa:cilogon.org,2010:/1.0/configuration/portal/"/>
                 < d:callbackUri>https://my.awesome.portal/portal/ready < /d:callbackUri>
                 < d:failureUri>https:// my.awesome.portal /portal/failure < /d:failureUri>
                 < d:portalName>My Awesome Portal< /d:portalName>
                 < d:successUri>https:// my.awesome.portal /portal/success < /d:successUri>
                < d:tempDirectory>none < /d:tempDirectory>
             < /rdf:Description>
         < /c:hasPortalParameters>

Note: The "https" in the successUri.  It MUST be a secure url.

Now, copy that file into the project's src/main/webapp/WEB-INF folder and rename it cfg.rdf

The point of entry into this application is the WelcomeServlet.  This amazingly simple servlet can be the baseline for whatever you want to do with it, but in this project we don't really need it.  You can delete the WelcomeServlet.java and the welcome.jsp or leave them in, it's up to you.  If you do remove it, be sure to remove the corresponding servlet mapping from the web.xml.  Later, when we create the apropriate Sign In link, we'll point it to the StartRequest servlet.

What happens behind the scenes is fully documented in the docs available at the CILogon website, but essentially what happens is that the servlets will contact CILogon and establish a trust before redirecting the user to the IdP selection page.  Have a look at the source code in the StartRequest.java file.

Next, let's take a look at SuccessServlet.java.

This is the servlet that will receive the cert back from the IdP with the user's identifying data, which Liferay will use to log them in.  In a future revision I'm going to see if I can eliminate that servlet altogether and incorporate that processing into the Extension for Liferay, but for now it's out here, separate.

What you'll need from the X509Certificate you get back from CILogon is the user's E-mail address and their name.  Then, you'll take that data and put it into cookies.  Adding to the existing code, here's the code I ended up with in the present method:

        String userEmail = "";
    SuccessfulState ss = (SuccessfulState) pState;
        request.setAttribute("cert", toPEM(ss.getX509Certificate()));
        request.setAttribute("key", toPKCS1PEM(ss.getPrivateKey()));
Collection certItems = ss.getX509Certificate().getSubjectAlternativeNames();

String certDetails = ss.getX509Certificate().getSubjectX500Principal().getName();

System.out.println("Details from cert are: " + certDetails);

Iterator itCertItems = certItems.iterator();

List entry = (List)itCertItems.next();
userEmail = (String)entry.get(1);
System.out.println("User Email received:" + userEmail);
        response.addCookie(buildCookie("CILOGON-USER_NAME", getUserName(certDetails)));
        response.addCookie(buildCookie("CILOGON-USER_EMAIL", userEmail));
        fwd(request, response, "success.jsp");

Note that we're anticipating a specific format for the incoming data.  (I left my debug statements in.)

The buildCookie method:

    private Cookie buildCookie(String cookieName, String cookieValue){
Cookie cookie = new Cookie(cookieName, cookieValue);

        return cookie;

Pretty straightforward, right?  Here's the getUserName method:

    private String getUserName(String details){
    String[] detailArray = details.split("=");
    String[] nameArray;
    String nameSegment = "";
    String name = "";

    for(int i = 0; i < detailArray.length; i++){
    nameSegment = detailArray[i + 1];
    nameArray = nameSegment.split(" ");
        name = nameArray[0] + " " + nameArray[1];
    return name;

And the isEmail method.  Note that it used a regex to ensure that it is getting a correctly formatted E-mail:

    private boolean isEmail(String value){
    String regex = "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";  
    CharSequence inputStr = value;  
    //Make the comparison case-insensitive.  
    Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);  
    Matcher matcher = pattern.matcher(inputStr);  
    return true;
    return false;

Now you can build this application using Maven install.  Don't deploy it yet though... There's one thing you need to do with Tomcat...

To be continued...

This project incorporates tools whose development was funded in part by the NIH through the NHLBI grant: The Cardiovascular Research Grid (R24HL085343)

CILogon and Liferay Part 1: Getting Started

A few months ago I posted a series of entries on this blog to help people get started using Shibboleth as an IdP for logging into a Liferay portal.  Well, it's time to revisit the topic only this time instead of using Shibboleth, we'll be using CILogon.

CILogon is similar to Shibboleth in that it connects the user to a separate Identity Provider for authentication.  A nice feature of CILogon is that it allows the user to choose from a list of available Identity Providers for use with the resource they're accessing.  For more information, visit the CILogon Website.

One thing to keep in mind is that some users may have multiple accounts across multiple Identity Providers.  For example, both Google and PayPal can appear in the list of IdPs and the authenticated user data that they return aren't necessarily going to match.  That means that the same user can come at the portal from multiple different IdPs with different authentication details.

For example, when I authenticate through CILogon on my test server, I can choose from Google, PayPal and Johns Hopkins University as my IdP.  I have accounts with all three of them, and each of them would return a different E-mail address and username after authenticating.  When you use CILogon, you need to keep that in mind, and make decisions on how you're going to handle that kind of situation.

In the case of this tutorial, we're going to ignore that since design decisions like that will vary by application, and implementing solutions will be unique.

One more caveat:  I'm still in the process of refining and fine tuning this process myself, so I won't pretend there isn't room for improvement.  As with all of my blog posts, I'm sharing the basics of what I've found to work, and I leave the elegance to the reader.

This particular setup will involve:

Operating System: (Linux) CentOS 5.6
Authentication Provider: CILogon 1.01
Servlet Container: Tomcat 6.0.29
Portal: Liferay 6.06
IDE: Eclipse Helios

(Yeah, I know Liferay is up to version 6.1 and Tomcat is on version 7, but this solution hasn't yet been tested in those environments.  Liferay 6.1 is different enough from 6.0 that I make no guarantees that this same version will work.)

Prep work:

  Before you go any further, you absolutely must obtain a certificate from CILogon if you don't already have one from a trusted Certificate Authority.  The process for doing so is detailed here.  This is not an optional step!  That means you also need to be doing this on a machine that has a static IP address and hostname.

Got it?  Alright.  Let's proceed.

You're also going to need Maven to build the CILogon portal servlet application.  If you don't already have the Maven plugin, download and install that.

Step 1: Install Liferay

If you haven't done so already, download Liferay 6.06 and install it according to the Liferay instructions.

Start it up and make sure it works, and that you're able to log in as the admin.  All set?  Good.

Step 2: Download the CILogon Portal App.

The way I did this was to download and customize the cilogon-portal-servlet project.  You can import it right into Eclipse using SVN.  In the File menu, click Import...  You'll then see a popup box where you can select the import source.  Under the SVN folder, select "Checkout Projects from SVN" and click "Next."  Choose "Create a new repository location" and enter the URL in the link at the beginning of this paragraph.  Click "Finish."

This can sometimes be a little tricky, so you might have to wrestle with it a little.  (I did, but I managed to get it to download into Eclipse eventually.)

Now that you have the project in your Eclipse, you can customize it to fit your needs.  Eventually you'll be installing this as a webapp in the same Tomcat where Liferay lives.  Make sure you can build it with Maven before moving on to the next step.

To be continued...