Liferay 5.2.3: custom URLs formats


Liferay SEO capabilities seems to be surprisingly weak when it comes to URL management. Consider an example: you’re trying to build a webapp that will be doing some abstract searches over some search data sources, and present the results on one page.

You want page to have URL like http://<host>[:<port>]/section/subsection/search/<keyword>[?someParam=<value>]
Particular goals: URL can be generated by other website that knows nothing of our Liferay-based portal internals, and it (URL) should be nice and bookmarkable.

On the page you want to have some portlets, provided by different development teams/vendors, that would get the keyword and present results. The portlets should be independent since new ones can be added over time, and you want to be able to order development of several new portlets in parallel via several independent vendors. Thus every portlet on page should be able to obtain <keyword> and <value> passed in URL to page.

So, what does Liferay provide?
1. URL patterns are /web/<organization>/<page> by default. There are a couple of tricks to handle “/web/<organization>” part (configuring virtual hosts etc), yet they’re not very intuitive.
2. Friendly URL mapper (Liferay 6 and Liferay 5 differ here BTW) that at best will allow you to have URLs like /web/guest/search/-/searchportlet_A/keyword/<keyword> – if your portlet with ID “searchportlet_A” is non-instanceable, this and only this particular portlet will get “keyword” parameter value from this URL.

I didn’t spend too much time digging in details of these solutions (and thus it might be I’m wrong about some details), but it’s obvious we’re not going to get far with this. So what can we do?

It turns out that Liferay has Tucky URL Rewriter filter in it, disabled by default. Using this filter we could map URLs that we want to a page that we want (doing it with forward of-course, no redirects from nice URL to ugly URLs or anything like this).
It will be a task for our portlet(s) to render proper custom URLs, but that’s not a big problem, it’s just nothing will facilitate us to do it. But well, this is the price of having absolutely custom format of URLs. Thus we can get our page by any URL we want.

But next question is – how to obtain outer URL and it’s parameters from portlets on a page?
Digging in Liferay 5 API revealed the somewhat obscure com.liferay.portal.util.PortalUtil class that provides static methods like getCurrentURL(PortletRequest) and PortalUtil.getHttpServletRequest(PortletRequest) + PortalUtil.getOriginalHttpServletRequest(ServletRequest).

Seems like we’ve got what we need, right? Not yet.
PortalUtil methods work Ok… until you enable forwarding via Tucky URL Rewrite filter. Once you enable it, URLs like “/c/layout/” will be returned instead of expected original URL that user typed into his browser’s address bar.
Obviously, author of the code didn’t expect that original request could already be a forwarded request. Well, not good – you did included Tucky URL Rewriter, didn’t you, so why using it is should come as something unexpected? (-;

Fortunately, sources of all the stuff are available, so I could quickly identify a problem and make a fix. Cheers to open-source!
We want our solution to be deployable to any Liferay 5 instance customers might already have, thus I didn’t put fixed into Liferay sources, but make a small “wrapper” for PortalUtil instead.

package com.mykola.makhin.liferay.portal.util;

import javax.portlet.PortletRequest;
import javax.servlet.http.HttpServletRequest;

import com.liferay.portal.util.PortalUtil;

public class LiferayUtil {

	public static String getOuterRequestUrl(PortletRequest portletRequest) {
		String result;

		HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(portletRequest);
		String forwardedRequestUri = (String) servletRequest.getAttribute("javax.servlet.forward.request_uri");
		String forwardedRequestQueryString = (String) servletRequest.getAttribute("javax.servlet.forward.query_string");
		if (forwardedRequestUri != null) {
			result = forwardedRequestUri;
			if (forwardedRequestQueryString != null) {
				result += "?" + forwardedRequestQueryString;
			}
		} else {
			result = PortalUtil.getCurrentURL(portletRequest);
		}

		return result;
	}

	public static String getOuterRequestParameter(PortletRequest portletRequest, String paramName) {
		HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(portletRequest);

		return PortalUtil.getOriginalServletRequest(servletRequest).getParameter(paramName);
	}
}

Ok, so we can obtain external URL and URL parameters with this utility, how do we put it all together?
Here’s the plan:
1. Enable and configure Tucky URL rewriter.
How to do this? We use Tomcat-bundled distribution of Liferay 5, so direct way would be to edit web.xml and urlrewrite.xml in “tomcat-6.0.18/webapps/ROOT/WEB-INF” folder, but more smart would be to deploy it using Liferay EXT environment .
In EXT go to “ext-web/docroot/WEB-INF”, edit web.xml and create+edit urlrewrite.xml files (note: don’t append -ext to file names).
Contents of web.xml will be merged with other contents that are in Liferay already, so what we put in web.xml file in EXT is just this:

<?xml version="1.0"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
        <filter>
                <filter-name>URL Rewrite Filter</filter-name>
                <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
                <init-param>
                        <param-name>logLevel</param-name>
                        <param-value>ERROR</param-value>
                </init-param>
                <init-param>
                        <param-name>statusEnabled</param-name>
                        <param-value>true</param-value>
                </init-param>
        </filter>
        <filter-mapping>
                <filter-name>URL Rewrite Filter</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>
</web-app>

Contents of urlrewrite.xml for our example will be something like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//EN" "http://tuckey.org/res/dtds/urlrewrite2.6.dtd">

<urlrewrite>
	<rule>
		<from>/section/subsection/search/([^\?]+)\?(.*)</from>
		<to type="forward">/web/myorganization/search?keyword=$1&amp;$2</to>
	</rule>
	<rule>
		<from>/section/subsection/search/([^\?]+)$</from>
		<to type="forward">/web/myorganization/search?keyword=$1</to>
	</rule>
</urlrewrite>

2. Now we can use LiferayUtil.getOuterRequestParameter(PortletRequest, “keyword”) and LiferayUtil.getOuterRequestParameter(PortletRequest, “anyParameter”) in our portlet to obtain parameters we need (note that in original URL “keyword” is not a parameter, but in rewritten it is, and thus getting it from forwarded request works just allright).

3. The only this left to us is to render URLs with proper format in our Portlets.
Since we can put any parameter in rewritten url (i.e. make something like <to type=”forward”>/web/myorganization/search?urlRewritten=true&amp;keyword=$1</to>) we can be aware whether portlet is on page accessed by nice SEO friendly URL or not, and correspondingly render either custom format SEO friendly URLs, or just do usual portlet render/action URLs.

That’s it.
I want to point out that resulting solution is quite easy to deploy to production environments that already have Liferay running.
Though EXT environment it a bit heavy, we can always ship only our two files. They can be written to clean distribution of EXT, downloaded straight from official site, and then ext can be deployed the usual way. And next time on same environment we don’t even need to do this again of course, we’ll only have to deploy our new/updated portlets.

Conclustion: I’ve spent a couple of days (partially, not the entire days he-he (-:) thinking and digging until I came up with 100% working solution (and relatively easily deployable too). I’m a bit surprised I could not google-up a solution like this, and especially surprised with what’s offered officially from Liferay (because rest of my experience with Liferay was rather positive so far). And curse Portlet APIs (d-:) for not even thinking about things like this.

Hopefully this post will save somebody time and effort of figuring out how to accomplish this task. After all, having a custom URL format for your webapp must be simple in year 2010, right? (-;
And once again, open source approach (as well as usage of standard tools – Tucky URL Rewriter in this case) saved time and effort, and actually made some things possible that otherwise would not be (or seem to be) so.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值