Portlets communication on the same page in render phase.

Once again I faced the same old problem: I have one portlet A which carries a value X and 2 other portlets on the same page B and C that needs this value X on their render phase. The problem exists, because also the portlet A has this value on its render phase. So, how can you transfer the value X from the render phase of the portlet A to the render phases of the portlets B and C?

According to this wiki article we cannot use the IPC methods because we have no action phase on portlet A. So, only two options remain:

  1. portlet session
  2. cookies
Lets have a look at the portlet session and lets assume we have made all the modifications on the portlets so, that the portlet A can set the value X in the session in application scope and the portlets B and C can read it.
There is a still a (big) problem: You cannot ensure that portlets A render phase will be executed before the render phases of portlet B and C.
But this time the search on the internet was fruitful. I found out that there is an element in liferay-portlet.xml which defines the order of the render phase of the portlets on the same page. At last. And the solution was always there, in the DTD. The hint of Olaf, to read the dtds, was correct.
So, the element is
1
<render-weight>1</render-weight>

According to the documentation in the DTD (liferay 6.1):

The default value of render-weight is 1. If set to a value less than 1, the portlet is rendered in parallel. If set to a value of 1 or greater, then the portlet is rendered serially. Portlets with a greater render weight have greater priority and will be rendered before portlets with a lower render weight. If the ajaxable value is set to false, then render-weight is always set to 1 if it is set to a value less than 1. This means ajaxable can override render-weight if ajaxable is set to false.

So, setting those element value to 3 for portlet A and to 2 for the portlets B and C will ensure that A will execute its render method before the other two portlets.

JCaptcha in Liferay (multiple instances per page)

Liferay supports simpleCaptcha and reCaptcha by default. The problem is that if you want more instances of a captcha in one page (say, you have a complicated registration form for different kind of users), then none of those captcha engines will satisfy you, because they do not support it.

You can of course try a workaround, like load the captcha with ajax when it is necessary, but in some cases, this is also not a acceptable solution.

The solution described in this post will allow you one instance per portlet, because the string that we use in the session to validate the user input against the captcha image is the session id. You are free to change that and use, for example, the form name, or something else, to have multiple instances in a portlet.

So, here is a solution with another captcha engine, JCaptcha. It is relative easy to use and straight forward.

We assume you have created a liferay portlet plugin project with a MVCPortlet.

Those are the steps you have to do:

  1. download JCaptcha jar and put it in the lib directory of your portlet project
  2. create a CaptchaServiceSingleton class
  3. create the serveResource method in your portlet and implement it to serve the captcha image
  4. create an action to validate the captcha image
  5. create a asp that show the image in a form were the user can type the letters of the image shown.

More or less, thats all you have to do.

Lets have a closer look at those steps.

Download JCaptcha and copy it in the lib dir

This should be an easy thing to do. The lib directory is in

1
docroot/WEB-INF/lib

Currently, the file name is “jcaptcha-1.0-all.jar”.

There is also one dependency: you have to add the “commons-collections.jar” in the “liferay-plugin-package.properties” file.

Create a CaptchaServiceSingleton class

JCaptcha need a singleton class for its own use. So, we create it. Lets give to this class the name “CaptchaServiceSingleton”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;

public class CaptchaServiceSingleton {

    public static ImageCaptchaService getInstance() {

        return instance;
    }

    private static final Log logger = LogFactoryUtil.getLog(CaptchaServiceSingleton.class);

    private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();
}

serverResource method

In the portlet class create the following method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
        private static final Log logger = LogFactoryUtil.getLog(TestCaptcha.class);

    @Override
    public void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse)
        throws IOException, PortletException {

        ThemeDisplay themeDisplay = (ThemeDisplay) resourceRequest.getAttribute(WebKeys.THEME_DISPLAY);

        byte[] captchaChallengeAsJpeg = null;
        // the output stream to render the captcha image as jpeg into
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            // get the session id that will identify the generated captcha.
            // the same id must be used to validate the response, the session id is a good candidate!
            String captchaId = resourceRequest.getPortletSession().getId();

            logger.info("GENERATING CAPTCHA IMAGE FOR " + captchaId);

            // call the ImageCaptchaService getChallenge method
            BufferedImage challenge =
                CaptchaServiceSingleton.getInstance().getImageChallengeForID(captchaId, themeDisplay.getLocale());


            // a jpeg encoder
            JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
            jpegEncoder.encode(challenge);

        }
        catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
        }
        catch (CaptchaServiceException e) {
            logger.error(e.getMessage(), e);
        }
        catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();

        HttpServletResponse response = PortalUtil.getHttpServletResponse(resourceResponse);

        // flush it in the response
        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType(ContentTypes.IMAGE_JPEG);

        ServletResponseUtil.write(response, captchaChallengeAsJpeg);
    }

validateCaptcha method

We will create a private method for the captcha validation and an action method that will be called on the submission of the form. The validation method will use the singleton calls we created on the first step.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    public void validateCaptcha(ActionRequest request, ActionResponse response) {

        if (validateCaptcha(request)) {
            SessionMessages.add(request, "Everything went fine");
        }
        else {
            SessionErrors.add(request, "Input is wrong. Please try again");
        }
    }

    public boolean validateCaptcha(PortletRequest request) {

        Boolean isResponseCorrect = Boolean.FALSE;
        // remenber that we need an id to validate!
        String captchaId = request.getPortletSession().getId();
        // retrieve the response

        String response = ParamUtil.getString(request, "j_captcha_response");
        // Call the Service method

        logger.info("captchaId:" + captchaId);
        logger.info("response:" + response);

        try {
            isResponseCorrect = CaptchaServiceSingleton.getInstance().validateResponseForID(captchaId, response);
        }
        catch (CaptchaServiceException e) {
            // should not happen, may be thrown if the id is not valid
            logger.error(e.getMessage(), e);
        }

        return isResponseCorrect;
    }

view.jsp file

So, now, the view… In our “view.jsp” file we put the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://alloy.liferay.com/tld/aui" prefix="aui" %>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>

<portlet:defineObjects />

<portlet:actionURL name="validateCaptcha" var="validateCaptchaUrl"></portlet:actionURL>

<aui:form action="${validateCaptchaUrl}" method="post" >
    <liferay-ui:success key="success-key" message="everything-went-fine"/>
    <liferay-ui:error key="error-key" message="input-is-wrong"/>
    <portlet:resourceURL var="serveCaptchaUrl"></portlet:resourceURL>  
    <img src="${serveCaptchaUrl}">
    <input type='text' name='j_captcha_response' value=''>
    <button type="submit">Submit</button>
</aui:form>

And that’s all.

If you need a refresh link you can add a link that runs a javascript function that removes and add the src of the captcha image. For example, using jQuery, this method could be like this:

1
2
3
4
        $("#<portlet:namespace/>refreshCaptcha").click(function(){
            $("#<portlet:namespace/>captchaImg").attr("src", "");
            $("#<portlet:namespace/>captchaImg").attr("src", "${serveCaptchaUrl}");
        });

Plugin Unit testing in Liferay 6.2

In the past I tried to find out how to unit test my plugins in liferay with no success. To be honest, I never tried much, and I am also a rookie in unit testing. I always wanted to learn to use test driven development but the pressure at work didn’t helped me to change to that direction. Perhaps now I will succeed.

The existing documentation on how to unit test plugins in liferay 6.2 is as good as none. There is some for 6.1, but it is not more valid in 6.2. So, if the things I wrote could be done in a better way (or are wrong), please notify me, I will appreciate any help.

I tried to write the document in a simple way, and to break it in steps, so that every part of the configuration needed for every case is easy to be understood.

So, I will break the article in five parts:

  1. Infrastructure: the folder structure should follow specific names, so that the sdk ant tool can find everything in tis place
  2. Unit Testing: a simple unit test
  3. Unit Testing with mockups: a more advanced unit test using a mockup library
  4. Integration Testing with Liferay core Services
  5. Integration Testing with custom Plugin Services *

(*) This section is not ready yet. I can’t find a way to load the custom services in the spring container, but I am working on that.

The ant tool in the liferay SDK searches for test classes in specific directories, so I decided to follow this structure also in the article, because this way it makes more sense.

Infrastructure

In your eclipse project (I use eclipse as my IDE) create a new folder with the name “test” and under it create the following tree:

1
2
3
4
5
6
7
8
9
10
11
+-- docroot
|
+-- test
   |
   +-- unit
   |   |
   |   +-- src
   |
   +-- integration
       |
       +-- src

Add the two “src” folders in your build path.

In unit/src you will place your unit test classes and in integration/src the integration test classes.

Having this infrastructure allows us to execute our unit and integration tests from the liferay sdk ant tool.

We have these options:

  • ant test : runs all the unit and integrations tests in the subfolders
  • ant test-unit : runs all unit tests
  • ant test-integration: runs all integration tests

If you like to see what those targets do, you can view the code in the file “build-common-plugin.xml”, which is located in the root folder of the liferay SDK. You have to run those targets from inside your project directory, using the projects build.xml.

After running one of the above commands for the first time, two new directories will be created, like this:

1
2
3
4
5
6
7
8
9
+-- docroot
|
+-- test
|
+-- test-classes
|
+-- test-results
|
+-- (liferay - existence under condition)

The folder “test-classe” contains your compiled classes and the folder “test-results” contains the test results in an xml form, so that those can be used in eclipse (try to double click on them) or in a continuous integration server (like jerkins).

If you use (that will be mention later in the article) the hypersonic database, you will see also a “liferay” directory on the same level, that contains all the data of this database.

Unit Testing

Let us now create a simple unit test for our project. All we need is to create a class in the unit/src folder, for example the “UnitTest.java” with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class UnitTest {

    @Before
    public void setUp()
        throws Exception {

    }

    @After
    public void tearDown()
        throws Exception {

    }

    @Test
    public void test() {

        fail("Not yet implemented");
    }

}

This code will always fail, but you can insert your code there and do your testing. After running the “ant unit” task:

1
2
3
4
5
6
7
8
9
10
test-cmd:
    [junit] Running UnitTest
    [junit] Testsuite: UnitTest
    [junit] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.565 sec
    [junit] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.565 sec
    [junit] Testcase: test(UnitTest):   FAILED
    [junit] Not yet implemented
    [junit] junit.framework.AssertionFailedError: Not yet implemented
    [junit]     at UnitTest.test(UnitTest.java:24)
    [junit] Test UnitTest FAILED

Unit Testing with mockups

If you want to create some useful unit tests, you will have to mock some classes. The liferay SDK has already included the libraries of PowerMock and Mockito when run nit the tests with the ant tool.

So, we need to add those libraries also in our eclipse project. The best way to do this was to define a linked folder with the path “../../lib”. In the linked folder you will find all the jars from the SDK. Select “mockito-all.jar” and all with the pattern “powermock-*.jar” and add those to your build path.

Now we are ready to use mocking in our unit tests. Try the following changes in the “UnitTest.java” file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.when;

import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.service.UserLocalServiceUtil;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserLocalServiceUtil.class)
public class UnitTest {

    @Before
    public void setUp()
        throws Exception {

    }

    @After
    public void tearDown()
        throws Exception {

    }

    @Test
    public void test()
        throws SystemException {

        PowerMockito.mockStatic(UserLocalServiceUtil.class);
        when(UserLocalServiceUtil.getUsersCount()).thenReturn(5);
        assertEquals(5, UserLocalServiceUtil.getUsersCount());
    }

}

After run nit “ant unit” you should get:

1
2
3
4
    [junit] Running UnitTest
    [junit] Testsuite: UnitTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.736 sec
    [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.736 sec

Integration Testing with Liferay core Services

If you want to use a core liferay service, for example the class UserLocalServiceUtil, you will need to initiate the portal spring container. You can do this by using the “InitUtil.initWithSpring()” method. But there a catch here: the class InitUtil is not part of the SDK. You can find it in the portal-impl.jar. Running the tests from the ant tool (which is part of the SDK) includes this library in the build path. But eclipse doesn’t know its existence. But there is a nice solution to this: just add download the source code of the portal, create a eclipse project from that and then create a project reference from your project to that source code. You will gain two things. First, you will have access to the InitUtil class, and to all the portal source, which is (till now) the best documentation you will find for liferay.

With the reference to the portal source you can now begin writing your tests. Add the spring initialisation code in the setUp method:

1
2
3
4
5
6
7
    @Before
    public void setUp()
        throws Exception {

        InitUtil.initWithSpring();

    }

This would work, but it will use the hybersonic database which is defined in the default portal.properties of the portal-impl.jar library. Perhaps its better to use a different database (one that you can control). To do so, you will need to create a “portal-ext.properties” file in the “test” directory, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.url=jdbc:mysql://localhost:3306/liferay?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.default.username=root
jdbc.default.password=root

hibernate.cache.use_query_cache=false
hibernate.cache.use_second_level_cache=false
hibernate.cache.use_minimal_puts=false
hibernate.cache.use_structured_entries=false
ehcache.portal.cache.manager.jmx.enabled=false

liferay.home="path to liferay portal home dir"
resource.repositories.root="path to app server home dir"/webapps/ROOT

# Disable the scheduler for Unit testing
scheduler.enabled=false

where:

  • jdbc.default* define the connection to the database
  • hibernate.cache* disables some hibernate stuff like the cache
  • “path to liferay portal home dir” is the path to your portal

Then write your integration test. Here a simple test:

1
2
3
4
5
6
7
8
9
10
11
12
13
    @Test
    public void test() {
        try {
            // returns all users of the portal
            List<User> users = UserLocalServiceUtil.getUsers(QueryUtil.ALL_POS, QueryUtil.ALL_POS);

            assertTrue("Users must not be empty", !users.isEmpty());

        }
        catch (SystemException e) {
            fail("Exception:" + e.getMessage());
        }
    }

And now you have your integration test working. After calling “ant integration” you get this:

1
2
3
4
5
    ...
    [junit] INFO  [main][DialectDetector:71] Determine dialect for MySQL 5
    [junit] INFO  [main][DialectDetector:136] Found dialect org.hibernate.dialect.MySQLDialect
    [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 34.456 sec
    [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 34.456 sec

Integration Testing with custom Plugin Services

Will follow in a future update of the article.

I posted in liferay forum the main problem I am facing…

“PermissionChecker not initialised” in scheduler job

When writing a scheduler job in liferay, you don’t have access to the the themeDisplay life in portlet methods. So you miss a lot of handy data.

If you like to call in your job code some of the local utility classes, you could become the following exception:

1
com.liferay.portal.security.auth.PrincipalException: PermissionChecker not initialized

This is because you don’t have the permission to make that call. Just use the following code before your code and everything will be fine.

1
2
3
4
5
6
7
8
9
Company companyqq = CompanyLocalServiceUtil.getCompanyByWebId("myCompanyWebId");
Role adminRole = RoleLocalServiceUtil.getRole(companyqq.getCompanyId(),"Administrator");
List<User> adminUsers = UserLocalServiceUtil.getRoleUsers(adminRole.getRoleId());

PrincipalThreadLocal.setName(adminUsers.get(0).getUserId());
PermissionChecker permissionChecker =PermissionCheckerFactoryUtil.create(adminUsers.get(0), true);
PermissionThreadLocal.setPermissionChecker(permissionChecker);

myMethod();

source: here

Custom simple captcha in custom portlet for Liferay

In the last month we had to add different captchas in some liferay sites. Some customers were ok with the simple captcha, that comes per default with liferay and some others wanted to have the recaptcha of google, that also comes with liferay (not so default). And there was a third category of clients that weren’t happy with a any of the above mentioned captchas and wanted a third solution.

In this post we will show how to add simple captcha in a form. To integrate simple catch in a form of a custom portlet is described in many articles like this one.

But there is not so many articles that describe how to make a captcha with a custom design and a refresh image button. So, here is a why way to implement it:

In the portlet class we need to change the code in two positions.

First in the serveResource method:

1
2
3
public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException {
  CaptchaUtil.serveImage(request, response);
}

The code above returns the image of the captcha.

And second in the action method:

1
2
3
4
5
6
7
8
9
10
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException {
  try {
    CaptchaUtil.check(actionRequest);
  } catch (CaptchaTextException e) {
    SessionErrors.add(actionRequest, CaptchaTextException.class.getName());
  } catch (CaptchaMaxChallengesException e) {
    SessionErrors.add(actionRequest, CaptchaMaxChallengesException.class.getName());
  }
  ...
}

The code above checks the value of the captcha value and throws exceptions if the input is invalid.

Next, the jsp code. We need to write the html that shows the image, the refresh button and the text field. In addition, we need a javascript for the refresh button to replace the image with the new one.

Of course, our code is an example of how the html could be. Its is not a ready to use design.

First, the html:

1
2
3
4
5
6
7
8
9
10
11
12
13
  <%-- The url that returns the image --%>
  <portlet:resourceURL var="captchaURL"></portlet:resourceURL>
  <label><liferay-ui:message key="captcha-text" /></label>
  <div style="float:left;">
    <%-- the name of the field should be captchaText --%>
    <aui:input label="" name="captchaText" size="10" type="text" value="">
      <aui:validator name="required" />
    </aui:input>
  </div>
  <%-- the refresh button %-->
  <a href="#" class="refreshCaptcha captcha-reload" style="text-decoration: none;"></a>
  <%-- the image --%>
  <img style="float:left;padding:0px !important;margin-left:10px;" alt='<liferay-ui:message key="text-to-identify" />' class="captcha" border=0 src="<%= captchaURL + "&" + new Random().nextInt(Integer.MAX_VALUE) %>" />

And the js code:

1
2
3
4
jQuery(".refreshCaptcha").click(function(evt){
  jQuery(".captcha").attr('src', '<%=captchaURL%>&force='+encodeURIComponent(Math.floor(Math.random()*Math.pow(2, 53))));
  evt.preventDefault();
});

A tip to solve many asset category problems

In case you are experiencing a strange behaviour in the Categories portlet of the control panel, like creating a category in vocabulary x and finding it in vocabulary y, you try to rebuild the category tree for this site.

The API of the liferay gives us a method for that job: AssetCategoryLocalServiceUtil.rebuildTree

The method has 2 parameters, but you need to find out the value only for the first one. This parameter is the groupId, which is the id of the site. If you don’t know it you can find it following the steps as described here.

But if you are not a programmer, how can execute this method? The solution is simple. As the Administrator, go to the “Server Administration” and then to the “script” tab. Here you can run scripts in different languages. Select “Beanshell” and in the text area give the following code:

1
2
import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
AssetCategoryLocalServiceUtil.rebuildTree(type_here_the_groupId, true);

and then type run.