Introduction to SAML 2.0
Slides from a tech talk I gave about SAML 2.0 and Liferay
This is a new cool feature I worked on with Brian and it’s coming on 6.1 as well as 6.0 EE SP2 and 5.2 EE SP6. With this feature you can add new struts actions to portal from a hook plugin and you can override any existing action with it.
There are two interfaces com.liferay.portal.kernel.struts.StrutsAction and com.liferay.portal.kernel.struts.StrutsPortletAction. The StrutsAction is used for regular struts actions like /c/portal/update_password and StrutsPortletAction is used for those that are used from portlets.
Let’s create a new simple hook to test it out. This hook will create a new struts path /c/portal/sample and wraps an existing struts action. Start by creating a new hook plugin in your plugins SDK. I’ll call it sample-struts-action.
./create.sh sample-struts-action
Next edit the liferay-hook.xml and add following fragment:
<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_1_0.dtd">
<hook>
<portal-properties>portal.properties</portal-properties>
<custom-jsp-dir>/META-INF/custom_jsps</custom-jsp-dir>
<struts-action>
<struts-action-path>/portal/sample</struts-action-path>
<struts-action-impl>com.liferay.samplestrutsaction.hook.action.SampleStrutsAction</struts-action-impl>
</struts-action>
<struts-action>
<struts-action-path>/message_boards/view</struts-action-path>
<struts-action-impl>com.liferay.samplestrutsaction.hook.action.SampleStrutsPortletAction</struts-action-impl>
</struts-action>
</hook>
Next we need to create the struts action like below:
package com.liferay.samplestrutsaction.hook.action;
import com.liferay.portal.kernel.struts.BaseStrutsAction;
import com.liferay.portal.kernel.util.ParamUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mika Koivisto
*/
public class SampleStrutsAction extends BaseStrutsAction {
public String execute(
HttpServletRequest request, HttpServletResponse response)
throws Exception {
String name = ParamUtil.get(request, "name", "World");
request.setAttribute("name", name);
return "/portal/sample.jsp";
}
}
Next create the second Struts action. This one will actually wrap ViewAction of message boards portlet.
package com.liferay.samplestrutsaction.hook.action;
import com.liferay.portal.kernel.struts.BaseStrutsPortletAction;
import com.liferay.portal.kernel.struts.StrutsPortletAction;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletConfig;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
/**
* @author Mika Koivisto
*/
public class SampleStrutsPortletAction extends BaseStrutsPortletAction {
public void processAction(
StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, ActionRequest actionRequest,
ActionResponse actionResponse)
throws Exception {
originalStrutsPortletAction.processAction(
originalStrutsPortletAction, portletConfig, actionRequest,
actionResponse);
}
public String render(
StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, RenderRequest renderRequest,
RenderResponse renderResponse)
throws Exception {
System.out.println("Wrapped /message_boards/view action");
return originalStrutsPortletAction.render(
null, portletConfig, renderRequest, renderResponse);
}
public void serveResource(
StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, ResourceRequest resourceRequest,
ResourceResponse resourceResponse)
throws Exception {
originalStrutsPortletAction.serveResource(
originalStrutsPortletAction, portletConfig, resourceRequest,
resourceResponse);
}
}
Then we need to create the JSP in docroot/META-INF/custom_jsps/html/portal/sample.jsp
Hello !
And lastly we need to create portal.properties in docroot/WEB-INF/src
auth.public.paths=/portal/sample
Now we are ready to deploy the plugin and see if it works. Just run ant deploy in your plugins sdk to deploy it.
You should see following in your tomcat console:
22:01:29,635 INFO [AutoDeployDir:167] Processing sample-struts-action-hook-6.1.0.1.war
22:01:29,638 INFO [HookAutoDeployListener:43] Copying web plugin for /Users/mika/Development/Liferay/git/bundles/deploy/sample-struts-action-hook-6.1.0.1.war
Expanding: /Users/mika/Development/Liferay/git/bundles/deploy/sample-struts-action-hook-6.1.0.1.war into /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299
Copying 1 file to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299/WEB-INF/classes
Copying 1 file to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299/WEB-INF/classes
Copying 1 file to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299/WEB-INF
Copying 1 file to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299/META-INF
Copying 12 files to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/webapps/sample-struts-action-hook
Copying 1 file to /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/webapps/sample-struts-action-hook
Deleting directory /Users/mika/Development/Liferay/git/bundles/tomcat-6.0.29/temp/20110117220130299
22:01:30,486 INFO [HookAutoDeployListener:49] Hook for /Users/mika/Development/Liferay/git/bundles/deploy/sample-struts-action-hook-6.1.0.1.war copied successfully. Deployment will start in a few seconds.
Jan 17, 2011 10:01:39 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory sample-struts-action-hook
22:01:39,727 INFO [PluginPackageUtil:1080] Reading plugin package for sample-struts-action-hook
22:01:39,759 INFO [HookHotDeployListener:432] Registering hook for sample-struts-action-hook
22:01:39,770 INFO [HookHotDeployListener:717] Hook for sample-struts-action-hook is available for use
Now try to access http://localhost:8080/c/portal/sample. It will ask you to sign in and once you sign in you should see the message Hello World! in your browser. You can add a paramer name to the url to change the message. If you access message boards it will print the message “Wrapped /message_boards/view action” in tomcat console and continue to render message boards as if nothing was changed.
Now our sample was really simple one. The return value from the execute method is the view where the request is dispatched next. This can be path to JSP, an existing struts forward or tiles definition. Returning null means that your action has handled the view already. Now you could try to return for instance portal.terms_of_use to display the terms of use.
You can download this sample plugin from svn://svn.liferay.com/repos/public/plugins/trunk/hooks/sample-struts-action-hook. The username is guest and password is empty.
UPDATE: We changed the API so that the original action is passed in so that you can also wrap it with your own logic instead of replacing. I also added a new hook property auth.public.paths so it allows you to set new public paths from hooks. I also added a StrutsPortletAction into to the sample and that demonstrates wrapping an existing action.
That’s a question I’ve head many times and in this post I will show you just how to do that. These instructions are for Liferay 6 CE GA3 Tomcat 6.0 bundle however you can use any app server supported by Terracotta but the location and some configuration might be slightly different. So to get started you need to download:
Next step is to install Liferay and Terracotta. For the purposes of this post I won’t go into great detail with the installation as both Terracotta and Liferay has good documentation. Basically the installation consist of unpacking the packages to a directory. From now on I will refer to those locations as LIFERAY_HOME and TERRACOTTA_HOME and inside LIFERAY_HOME we will have tomcat directory which I will refer as TOMCAT_HOME. Normally you would also install Liferay and Terracotta in separate servers but I will post a separate post addressing the recommended architecture. For now we can install everything on the same machine and run Terracotta with default configuration for development purposes.
Normally when clustering Liferay you need to address following components: EhCache and Hibernate, Quartz Scheduler, Document Library, Search Engine and optionally Session Replication. For Document Library and Search Engine Terracotta doesn’t offer anything new so you make those centrally available the same way as before. For example SAN for DL and SOLR for Search and Indexing. So we are left with EhCache and Hibernate, Quartz and Session Replication that we can address with Terracotta.
ehcache.multi.vm.config.location=/my-ehcache/liferay-multi-vm-terracotta.xml net.sf.ehcache.configurationResourceName=/my-ehcache/hibernate-terracotta.xml hibernate.cache.region.factory_class=net.sf.ehcache.hibernate.EhCacheRegionFactory
org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore org.quartz.jobStore.tcConfigUrl = localhost:9510
#org.quartz.jobStore.dataSource=ds #org.quartz.jobStore.isClustered=false #org.quartz.jobStore.misfireThreshold=60000 #org.quartz.jobStore.tablePrefix=QUARTZ_ #org.quartz.jobStore.useProperties=false
This is highly container specific so refer to Terracotta documentation for specific instructions. Following steps are for Tomcat 6.0.
<Valve className="org.terracotta.session.TerracottaTomcat60xSessionValve" tcConfigUrl="localhost:9510"/>
Testing your configuration is simple:
TERRACOTTA_HOME/bin/start-tc-server.sh
TOMCAT_HOME/bin/startup.sh
2010-09-01 21:35:40,059 INFO - Terracotta 3.3.0, as of 20100716-140706 (Revision 15922 by cruise@rh5mo0 from 3.3) 2010-09-01 21:35:40,566 INFO - Successfully loaded base configuration from server at 'localhost:9510'.
TERRACOTTA_HOME/bin/dev-console.sh
Now as you can see it is quite easy the cluster Liferay with Terracotta express installation. Now if you want to use the DSO approach it is whole another beast as it involves tedious instrumentation. If you are a Liferay EE customer and want to get supported version of both Liferay and Terracotta contact your Liferay sales rep and ask about Liferay Terracotta Edition.
Freemarker is a template language very similar to Velocity. Starting from Liferay 6.0 Liferay supports also Freemarker templates in themes and Web Content templates. In this post I will show how you can use Freemarker in your themes.
To get started you’ll need Liferay Portal 6.0 GA3 as well as corresponding Plugins SDK. Once you have setup your Portal and Plugins SDK we can start by creating a new theme plugin in PLUGINS_SDK_ROOT/themes folder.
To create the theme issue following command:
./create.[sh|bat] my-freemarker "My Freemarker"
Then go to my-freemarker-theme directory and open build.xml in your favorite editor.
In build.xml add theme.type property with value ftl above theme.parent property like this:
<property name="theme.type" value="ftl"></property> <property name="theme.parent" value="_styled"></property>
Then you need to create docroot/WEB-INF/liferay-look-and-feel.xml with following contents:
<?xml version="1.0"?> <!DOCTYPE look-and-feel PUBLIC "-//Liferay//DTD Look and Feel 6.0.0//EN" "http://www.liferay.com/dtd/liferay-look-and-feel_6_0_0.dtd"> <look-and-feel> <compatibility> <version>6.0.0+</version> </compatibility> <theme id="my-freemarker-theme" name="My Freemarker"> <template-extension>ftl</template-extension> </theme> </look-and-feel>
Now you run:
ant deploy
Congratulations you’ve just made your first Freemarker based theme. Now you can override base theme files in docroot/_diffs folder just as you would normally except template files now have extension .ftl instead of .vm.
Freemarker syntax is slightly different from Velocity and it is much more strict. With Freemarker you won’t be able to get a way with trying to use undefined variables and you should also note that null value means it’s undefined. To test if value exists you can use double question mark after the variable name like this:
<#if someVariableName??> Variable exists </#if>
For full syntax reference check out Freemarker website.
Most of the variables present for Velocity templates are also available for Freemarker templates. Only Velocity specific tools were removed you can accomplish everything and more with Freemarker build-ins. Here’s some examples how to format a java.util.Date type variable with Freemarker build-ins:
${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")} ${lastUpdated?string("EEE, MMM d, ''yy")} ${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")}
You can find all the variables available for Freemarker templates from com.liferay.portal.freemarker.FreeMarkerVariables class and docroot/html/themes/_unstyled/init.ftl
Most of the macros available to Velocity templates are also available for Freemarker templates. The only difference is the syntax how they are used. We provide a macro library with namespace liferay so that it won’t get mixed with your own macros. You can take a look at portal-impl/src/FTL_liferay.ftl to see full list of macros and use it as an example to build your own macros. Here are some commonly used macros:
<@liferay.css file_name=“some.css” /> <@liferay.js file_name=“some.js” /> <@liferay.language key=“my-key” /> <@liferay.breadcrumb /> <@liferay.docbar />
Yes, you read it correctly. You can use taglibs in your Freemarker templates. This is something unique to Freemarker and it is limited to only templates in themes. To import a portal taglib to your template just add following line to your template:
<#assign aui=PortalJspTagLibs["/WEB-INF/tld/liferay-aui.tld"]>
Now you can use any tag within that taglib just if it was a macro library. Here’s an example how to add a Alloy UI input field:
<@aui.input name=“aStringLiteral” label=“Test” />
Have fun trying this out and if you find any glitches do report them to our issue tracker.
Starting to recover from jetlag after a two week trip Los Angeles and Liferay retreat. One of the things we finally made some progress during the developer retreat is providing official maven artifacts for Liferay as well as porting our plugins sdk to Maven. Things are not quite completed but I will provide some instructions here for all early adopters.
So our goal is to provide our CE releases through our own public repository as well as provide means for our EE customers to install the EE versions artifacts to their local maven repository.
If you have ever worked with enterprise projects using maven you already know how important a local maven repository and proxy is. For those not so familiar with Maven a proxy is a server that proxies your requests to public Maven repositories and caches the artifacts locally for faster and more reliable access. Most maven proxies can also host private repositories used for hosting your company’s private artifacts. Having a local proxy / repository makes your maven builds much faster and more reliable than accessing remote repositories that might even sometimes be unavailable.
First step is to install and setup Nexus. Nexus is a open source maven repository manager that can proxy to other repositories as well as host repositories. If you just want to try things locally you can skip this step.
Now you have a repository ready for Liferay’s Maven artifacts. Next step is to configure your maven to be able to upload artifacts to that repository.
Open your $HOME/.m2/settings.xml (if the file does not exist create it). Add the servers segment to your settings.xml
<?xml version="1.0" encoding="UTF-8"?> <settings> <servers> <server> <id>liferay</id> <username>admin</username> <password>admin123</password> </server> </servers> </settings>
You might also want to make your Nexus as your maven proxy. To do that just add following xml segment to your settings.xml right before servers element.
<mirrors> <mirror> <id>local</id> <name>Local mirror repository</name> <url>http://localhost:8080/nexus/content/groups/public</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors>
Next we will install the Liferay Maven artifacts to your repository. First you need to checkout Liferay code from the SVN.
svn --username guest co svn://svn.liferay.com/repos/public/portal/trunk portal-trunk
Guest user does not require password.
Then create a release.${username}.properties file and add
maven.url=http://localhost:8080/nexus/content/repositories/liferay-ce-snapshots
Build Liferay artifacts by running
ant clean start jar
Now you can deploy the Liferay artifacts to your maven repository by running
ant -f build-maven.xml deploy-artifacts
If you only want to have them locally without a maven repository you can run the install task instead of deploy
ant -f build-maven.xml install-artifacts
Now you can add Liferay dependencies to your maven project. Following artifacts are available:
<dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-client</artifactId> <version>5.3.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-impl</artifactId> <version>5.3.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-kernel</artifactId> <version>5.3.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-service</artifactId> <version>5.3.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-web</artifactId> <version>5.3.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>util-bridges</artifactId> <version>5.3.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>util-java</artifactId> <version>5.3.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>util-taglib</artifactId> <version>5.3.0-SNAPSHOT</version> </dependency>
To take full advantage of Maven we are porting the functionality of out ant based Plugins SDK to Maven. To use it you need to install it locally. To install the Liferay maven plugins and archetypes go into support-maven folder and run
mvn install
Now the Liferay Maven SDK is installed and ready to use. We’ve implemented a portlet archetype and deployer plugin.
Move to the folder where you want to create your portlet and run
mvn archetype:generate
From the list select liferay-portlet-archetype and provide your project groupId, artifactId and version for the portlet project.
You’re portlet project’s pom.xml has two properties liferay.auto.deploy.dir and liferay.version. These properties are usually moved to your parent pom.xml or settings.xml so that you don’t have to adjust them for every single plugin you create. Set the liferay.auto.deploy.dir to point to the Liferay autodeploy directory of your Liferay bundle. This is where the deploy plugin will copy your portlet. Now you are ready deploy your newly created portlet. You can deploy it by running
mvn liferay:deploy
We are also in the process of adding archetypes for themes, hooks and layouts as well as providing portlet archetypes for different types of portlets like JSP, Spring MVC, JSF etc. I will blog about it once they are done.
A special thanks goes to Thiago Moreira and Brian Chan for making this possible. Also for the community and customers for putting pressure to have this done.
If you are using 5.2.3 CE and want to take advantage of Maven for building Liferay portlets Milen Dyankov a Liferay community member has done also great work on a Maven SDK for 5.2.3 CE. You can find more about it from GitHub