<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <title>Ray Augé</title>
  <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/rss" />
  <subtitle>Ray Augé</subtitle>
  <entry>
    <title>Embedding portlets in themes on Liferay</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/embedding-portlets-in-themes-on-liferay" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/embedding-portlets-in-themes-on-liferay</id>
    <updated>2011-10-21T19:32:19Z</updated>
    <published>2011-10-21T14:28:23Z</published>
    <summary type="html">&lt;style type="text/css"&gt;
.portlet-blogs pre {white-space: pre-wrap !important;}
&lt;/style&gt;
&lt;p&gt;
	Liferay has long had the ability to embed portlets in themes. This is convenient for implementors to get highly functional design into the look and feel of a site. In my years at Liferay I've seen and heard many different attempts at doing this with various levels of success. There are a number of things to consider when embedding portlets in the theme and the same method does not apply in all cases.&lt;/p&gt;
&lt;p&gt;
	The original motive behind embedded portlets was for integrating WCM content or simple functional features such as Search or Language selection. As such the complexity of these portlets was understood to be low and without significant performance costs either due to not having any direct service calls (search &amp;amp; language selection portlets don't have any initial service calls), or having service calls which were highly cached (such as is the case with web content).&lt;/p&gt;
&lt;p&gt;
	There is more than one technique for embedding portlets into the theme, and several different issues to consider when choosing any of those.&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:16px;"&gt;&lt;strong&gt;Method One: Using&amp;nbsp;&lt;code&gt;$theme.runtime()&lt;/code&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
	This is by far the most common method. There are of course a few gotchas with this method.&lt;/p&gt;
&lt;p&gt;
	1) it renders the portlet synchronously in the theme regardless of the ajaxable settings on the portlet ( via liferay-portlet.xml).&lt;/p&gt;
&lt;p&gt;
	This means if the portlet is expensive to render because it a) has lots of data to process on render, b) uses synchronous web service calls, or c) calculates the fibonacci sequence up to a million,&amp;nbsp;&lt;strong&gt;DON'T EMBED IT IN THE THEME&lt;/strong&gt;! You're just killing the performance of your portal whenever that theme is on any page.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;Rule of Themes #1: &lt;/strong&gt;This is a good rule of thumb to follow regardless of embedded portlets or not: "Themes should be SUPER&amp;nbsp;FAST! They should be the fastest component of your portal view at any given time."&lt;/p&gt;
&lt;p&gt;
	If you don't follow this rule how will you expect the experience to be once you start actually putting more things on the page? It's a different topic entirely, but you should generally performance test your theme with no portlets on the page. Once you know it's blazing fast then proceed to test your portlets' performace.&lt;/p&gt;
&lt;p&gt;
	But I digress! Back to portlets embedded in the theme.&lt;/p&gt;
&lt;p&gt;
	2) portlets rendered in the theme don't have their js/css resources loaded at the top of the page with all the rest of the portlets. Liferay doesn't yet implement "streaming portlet" mode (this is optional in the spec, this doesn't mean Liferay is not fast, it's just a name they chose for the feature of calling the portlet's header altering methods separately from the rendering methods). So the issue with this is that if you embed a portlet that uses something like JSF dynamic javascript features and there happens to be more than one of this type of portlet on the page, the ordering of these dynamic resources may get fouled up and cause general mayhem to their behavior.&lt;/p&gt;
&lt;p&gt;
	On the other hand, I would argue that such portlets are already, by their very nature, TOO complex and expensive to be embedded in the theme. They are&amp;nbsp;quickly diverging from the rule above that the theme should be SUPER FAST!&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;Rule of Themes #2: &lt;/strong&gt;"Only use&amp;nbsp;&lt;strong style="font-size: 16px; line-height: 24px; "&gt;&lt;code&gt;$theme.runtime()&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;to embed portlets that are extremely fast. If they make expensive service calls, MAKE SURE those are not calls that happen all the time, and MAKE SURE they use great caching."&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	&lt;strong style="font-size: 16px; line-height: 24px; "&gt;Method Two: Ajax / Iframe!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	This is a "currently" seldom used method, but one that I would HIGHLY recommend using! Why?&lt;/p&gt;
&lt;p&gt;
	1) It's Asynchronous with the rendering of the theme. This is HUGE! It means that regardless how slow your portlet(s) is(are), the general experience of the portal will remain fast!&lt;/p&gt;
&lt;p&gt;
	2) It means that you can still put that fairly complex portlet into the theme without killing the overall performace.&lt;/p&gt;
&lt;p&gt;
	3) It doesn't suffer from the limitation of the resource loading that the&amp;nbsp;&lt;strong style="font-size: 16px; line-height: 24px; "&gt;&lt;code&gt;$theme.runtime()&lt;/code&gt;&lt;/strong&gt; method suffers from.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;So how do I do it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	Ok, so generally what you do to embed a portlet via ajax or iframe is to create a portlet url (either server side or client side) and then request for it, placing it somewhere in the page. With an iframe you can of course interact with the portlet in place. If you use an ajax call and embed the output of the portlet directly it will of course cause page refresh.&lt;/p&gt;
&lt;p&gt;
	Here is the code for doing it with the iframe:&lt;/p&gt;
&lt;p&gt;
	&lt;script src="https://gist.github.com/1304533.js?file=portal_normal.vm.html"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	One thing you'll note with this code is that it's making the assumption that the portlet is at the current page! But it's not on the current page yet!&lt;/p&gt;
&lt;p&gt;
	There are a couple of ways of handling this.&lt;/p&gt;
&lt;p&gt;
	1) The portlet is specifically designed to be in the theme and should be visible to anyone seeing the page.&lt;/p&gt;
&lt;p&gt;
	In this case I recommend setting these attributes in your liferay-portlet.xml file:&lt;/p&gt;
&lt;p&gt;
	&lt;script src="https://gist.github.com/1304533.js?file=liferay-portlet.xml"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;
	These will allow your portlet to safely added to any page in the portal automatically (otherwise you may get permission issues because the user viewing the portlet may not have permission and all that jazz.)&lt;/p&gt;
&lt;p&gt;
	2) You can't do as above because you still want to control permissions of the portlet using roles.&lt;/p&gt;
&lt;p&gt;
	In this case, you'll have to add the portlet to something like a hidden page, from which you can manage it's permissions. When creating the url in the code above, you'll then use the page "plid" of that target page instead of the current one.&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	Well, I hope that helps!&lt;/p&gt;
&lt;p&gt;
	I hope that I hear far less talk about&amp;nbsp;&lt;strong style="font-size: 16px; line-height: 24px; "&gt;&lt;code&gt;$theme.runtime()&lt;/code&gt;&lt;/strong&gt;issues and Liferay performance problems that end up being directly related to expensive operations embedded in the theme.&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	And please heed these words:&lt;/p&gt;
&lt;p&gt;
	"Embed portlets with impunity! Just make sure to do it carefully!"&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-10-21T14:28:23Z</dc:date>
  </entry>
  <entry>
    <title>Introducing Arkadiko, a bridge between Spring and OSGi</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/introducing-arkadiko-a-bridge-between-spring-and-osgi" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/introducing-arkadiko-a-bridge-between-spring-and-osgi</id>
    <updated>2011-10-18T18:42:33Z</updated>
    <published>2011-10-18T18:33:46Z</published>
    <summary type="html">&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
	Spring's dependency injection framework is the picture of ubiquity in the java&amp;nbsp;world. As such it aims to drive developers toward declarative, component driven&amp;nbsp;design and lose coupling. But, it is largely a static container. The class space&amp;nbsp;is flat and once started there is little means for dynamic change.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	OSGi is "the" modularity framework for java. It supports dynamic change via&amp;nbsp;strict classloader isolation and packaging metadata. It also aims to drive&amp;nbsp;developers toward modular design. But although it is very powerfull it is&amp;nbsp;also complex and learning it's intricacies can be daunting.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	These two paradigms have recently undergone a merge or sorts in such that&amp;nbsp;within OSGi frameworks one can use the declarative nature of Spring (via Spring&amp;nbsp;DM and more recently standardised as Blueprint) to configure and wire together&amp;nbsp;the interdependencies of components as they come and go from the OSGi framework.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	So how does &lt;strong&gt;this&lt;/strong&gt; project, &lt;strong&gt;Arkadiko&lt;/strong&gt; come into play?&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	First allow me to explain briefly the current state of the world as I understand&amp;nbsp;it (this is my personal assessment).&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Given the vast number of Spring based projects and the most recent&amp;nbsp;up-surge in the desire to benefit from OSGi's dynamic modularity it has become&amp;nbsp;clear that there is a understandable difficulty in moving projects of any&amp;nbsp;complexity to OSGi from a pure Spring architecture. The issue is that there are&amp;nbsp;some design changes that must be made in moving traditional java applications,&amp;nbsp;including Spring based ones, to OSGi. OSGi has a puritanical solution to a vast&amp;nbsp;number of problems caused by the traditional architectures, but in order to gain&amp;nbsp;from those solutions a considerable amount of redesign has to be done.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	What to do?&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	There are several well known projects and individuals promoting methodologies&amp;nbsp;and best practices to help undertake the considerable amount of work that such a&amp;nbsp;migration could potentially involve.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	A recent presentation by BJ Hargrave (IBM) and Peter Kriens (aQute)&amp;nbsp;(&lt;a href="http://www.slideshare.net/bjhargrave/servicesfirst-migration-to-osgi"&gt;http://www.slideshare.net/bjhargrave/servicesfirst-migration-to-osgi&lt;/a&gt;) defines&amp;nbsp;the concept of "services first" migration methodology. This methodology suggests&amp;nbsp;that the first steps in the migration is to re-design the existing architecture&amp;nbsp;such that it's based on a services oriented design. They offer some insight into&amp;nbsp;how that is accomplished paraphrased over several different articles about&amp;nbsp;μservices (micro-services) and I won't go into detail here about all that.&amp;nbsp;Suffice it to say that once this has been accomplished it becomes far simpler to&lt;/div&gt;
&lt;div&gt;
	isolate portions/groupings of code that form logical components into modules&amp;nbsp;which use or publish services in well defined manner to subsequently be turned&amp;nbsp;into OSGi bundles.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Also, a core developer of the Apache Felix project, Karl Pauls, has recently&amp;nbsp;released a library called PojoSR (&lt;a href="http://code.google.com/p/pojosr/"&gt;http://code.google.com/p/pojosr/&lt;/a&gt;) based on&amp;nbsp;some of the Apache Felix project code that itself does not implement a full OSGi&amp;nbsp;framework container, but essentially provides an active registry which scans the&amp;nbsp;class space for recognizable "services" and effectively tries to wire those&amp;nbsp;together or a the very least provide a central access point for those services.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	I have no in-depth knowledge on either of those two topics but I highly suggest&amp;nbsp;taking some time to review both because they present options for anyone&amp;nbsp;entertaining the notion for undertaking such a migration to OSGi, and several&lt;/div&gt;
&lt;div&gt;
	options are always welcome.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Finally we come to Arkadiko!&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Arkadiko is a small bridge between Spring and OSGi.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Arkadiko is an attempt to provide an new migration option, and borrows ideas&amp;nbsp;from both of the above and tries to marry into those the concept of "quick wins"&amp;nbsp;(such as immediate access to OSGi features) and "time to evolve".&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&lt;strong&gt;Quick Wins:&lt;/strong&gt; Often the light at the end of the tunnel seems awfully far away.&amp;nbsp;When reviewing the scope of the migration, it may seem like an eternity before&amp;nbsp;the benefits will pay off. So, Arkadiko gives you OSGi right now! How does it do&lt;/div&gt;
&lt;div&gt;
	that? It simply provides a means to wire an OSGi framework into your current&amp;nbsp;spring container. But that in itself doesn't help and so it also dynamically&amp;nbsp;registers all your beans as services and at the same time registers a&amp;nbsp;OSGi ServiceTracker for each one. It does this so that if matching services are&amp;nbsp;published into the OSGi framework they are automatically wired in place of those&amp;nbsp;original beans. You get OSGi right away! It also means that the OSGi framework&amp;nbsp;has access to all your beans and can use those as regular services.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&lt;strong&gt;Time to Evolve:&lt;/strong&gt; The other benefit is that you now have time to evolve your&amp;nbsp;platform into OSGi as you see fit, and as time allows, moving components slowly&amp;nbsp;from outside the OSGi framework, into the OSGi framework as re-design is&amp;nbsp;completed by component. This way you gain the benefit where you can get to it quickly. Also, those nasty libraries which have yet to be ported&amp;nbsp;or are still known to not live happily inside of OSGi framework can remain&amp;nbsp;outside thr framework, consumed and wired in from within the container, until&amp;nbsp;such a time as they evolve their own OSGi solutions.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Arkadiko is very small and very simple! It only comprises 5 classes in total&amp;nbsp;(one is an exception, one is constants, one is a util class, the real work is&amp;nbsp;done by two classes).&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Adding Arkadiko to your existing spring configurations is as simple as adding a&amp;nbsp;single bean:&lt;/div&gt;
&lt;pre&gt;
&amp;lt;bean class="com.liferay.arkadiko.BridgeBeanPostProcessor"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;property name="framework"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;!-- some factory to get an instance of org.osgi.framework.launch.Framework --&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/property&amp;gt;
&amp;lt;/bean&amp;gt;

&lt;/pre&gt;
&lt;div&gt;
	For more details and to see the code please go to&amp;nbsp;&lt;a href="https://github.com/rotty3000/arkadiko"&gt;https://github.com/rotty3000/arkadiko&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	Feedback is very appreciated!&lt;/div&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-10-18T18:33:46Z</dc:date>
  </entry>
  <entry>
    <title>Random Liferay Code Snippets</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/random-liferay-code-snippets" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/random-liferay-code-snippets</id>
    <updated>2011-11-01T12:36:49Z</updated>
    <published>2011-10-12T13:45:03Z</published>
    <summary type="html">&lt;p&gt;
	I've taken to using &lt;a href="http://github.com"&gt;github.com&lt;/a&gt;'s gist service quite alot recently and so I wanted to just spread the word that there are some handy nuggets appearing there.&lt;/p&gt;
&lt;p&gt;
	Here's the url&amp;nbsp;&lt;a href="https://gist.github.com/rotty3000"&gt;https://gist.github.com/rotty3000&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	Examples:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1281235"&gt;list open and accessible sites (that a user could join, or may already be part of)&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1265957"&gt;a simple localization hook&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1262994"&gt;embed navigation tag directly without portlet&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1262555"&gt;get all RatingsEntry by user and asset type&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1248970"&gt;embed a portlet via js&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1224272"&gt;simple sample client for fileupload to dl, requires commons-httpclient and it's dependencies&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1223463"&gt;to add localizations in a theme, add this compile section your your build.xml and the languages in the usual location&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1214263"&gt;simple dialog (inline popup) with encoded (safe) content from a bean&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1281325"&gt;setting look and feel settings on a non-instancable portlet embedded in the theme&lt;/a&gt; (velocity and freemarker)&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1287646"&gt;portlet urls from PHP portlet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	From others:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		&lt;a href="https://gist.github.com/1119898"&gt;How to Mavenize Liferay Plugins Ant build.xml&lt;/a&gt; (Kamesh Sampath)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	I hope those prove useful. I'll continue putting things there as I come across them.&lt;/p&gt;
&lt;p&gt;
	Also, there is an RSS feed of those if you want as well &lt;a href="https://gist.github.com/rotty3000.atom"&gt;&lt;img alt="" src="http://cdn.www.liferay.com/osb-theme/images/common/rss.png" style="width: 16px; height: 16px; " /&gt;&lt;/a&gt;. (won't include ones I add by others)&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-10-12T13:45:03Z</dc:date>
  </entry>
  <entry>
    <title>Liferay Portal &amp; OSGi</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-portal-&amp;-osgi" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-portal-&amp;-osgi</id>
    <updated>2011-09-04T17:03:27Z</updated>
    <published>2011-09-03T18:05:47Z</published>
    <summary type="html">&lt;p&gt;
	Over the past several months I've been working on integrating an OSGi framework into Liferay Portal.&amp;nbsp;The reasons for doing so are varied but include solutions to app server/servlet container dependency, library version conflicts, runtime module lifecycle, advanced extensibility, and so on.&lt;/p&gt;
&lt;p&gt;
	If you are not familliar with OSGi, I recommend visiting the &lt;a href="http://www.osgi.org/"&gt;OSGi web site&lt;/a&gt;, and in particular reading the &lt;a href="http://www.osgi.org/About/WhyOSGi"&gt;Benefits of Using OSGi&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	The first publicly available code of this integration is available as an&amp;nbsp;&lt;a href="https://github.com/rotty3000/liferay-portal/tree/OSGi"&gt;OSGi branch on my fork&lt;/a&gt; of the &lt;a href="https://github.com/liferay/liferay-portal"&gt;liferay-portal&lt;/a&gt; project on &lt;a href="https://github.com"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	The url of the branch is:&amp;nbsp;&lt;a href="https://github.com/rotty3000/liferay-portal/tree/OSGi"&gt;https://github.com/rotty3000/liferay-portal/tree/OSGi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	I'd like to encourage anyone having an interest in this work to clone the branch and send me feature improvements, bug fixes, etc, via git pull request, or even to just send me comments here.&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;Things I've done to date:&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		I'm using Eclipse Equinox 3.7 (but it works with any 4.1 or greater implementation of OSGi with no code changes, I tested Knopflerfish 3.2.0 and it worked very well, I'm sure Felix would work equally well).&lt;/li&gt;
	&lt;li&gt;
		Published all the services and beans of the portal's spring context as services in the framework so they can be used by bundles.&lt;/li&gt;
	&lt;li&gt;
		Developed a management portlet available in the control panel where the portal administor can add/remove/start/stop/update bundles, see all the bundle headers, all the service registrations and all service references of any bundle. (I'd like to eventually add bundle dependency graphing as well).&lt;/li&gt;
	&lt;li&gt;
		Make sure logging works (this can be tricky in OSGi due to libraries not due to OSGi).&lt;/li&gt;
	&lt;li&gt;
		Setup an auto deploy listener that will inspect any jar/war file dropped into the portal deploy folder that are OSGi bundles and automatically install them into the framework (and try to start them).&lt;/li&gt;
	&lt;li&gt;
		Setup a bridge Servlet which is wired into the OSGi HttpService to provide HttpServlet request dispatching for any servlets or jsps registered in the framework. (This feature depends on a bundle I created which I'm going to release in the comming days, in an OSGi branch of my&amp;nbsp;fork of the&amp;nbsp;&lt;strong&gt;liferay-plugins&lt;/strong&gt; repository. The bundle depends on the eclipse equinox http servlet package which is available in the Equinox 3.7 zip.)&lt;/li&gt;
	&lt;li&gt;
		I've successfully tested various code modification scenarios such as replacing a service implementation and replacing a Util implementation, with all the virtues of OSGi bundle lifecycle goodness.&lt;/li&gt;
	&lt;li&gt;
		I've successfully deployed and used the recently released&amp;nbsp;&lt;a href="http://www.eclipse.org/gemini/blueprint/"&gt;Gemini Blueprint&lt;/a&gt;&amp;nbsp;implementation of the OSGi Blueprint specification (which you might call&amp;nbsp;Spring for OSGi). It's a simple matter of deploying the 3 main bundles.&lt;/li&gt;
	&lt;li&gt;
		I've added BND lib to the portal so that it can be used for various OSGi packaging tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;Things I'd like to do next:&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		Make some changes to the Plugins SDK so that plugins can (optionally) be generated as OSGi bundles.&lt;/li&gt;
	&lt;li&gt;
		Support deploying the various bundle-ized&amp;nbsp;plugins into the framework.&lt;/li&gt;
	&lt;li&gt;
		Support all the spring wiring of ServiceBuilder services found in plugins correctly.&lt;/li&gt;
	&lt;li&gt;
		Setup a portal packaging system so that we can bundle as set of bundles/plugins so there is no need to assemble and install them manually.&lt;/li&gt;
	&lt;li&gt;
		Modify the Liferay repository mechanism so that it supports bundles and implements either auto update or auto detectiong of possible updates for bundles in the repository (and also resolves/downloads dependencies).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	Here are a couple of images of the OSGi Admin portlet.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;List of bundles:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;img alt="" src="http://www.liferay.com/documents/10529/7907517/osgi-1.png" style="width: 600px; height: 424px; " /&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;View of a Bundle:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;img alt="" src="http://www.liferay.com/documents/10529/7907517/osgi-2.png" style="width: 600px; height: 450px; " /&gt;&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	I'll be presenting this work in&amp;nbsp;a workshop at the upcoming &lt;a href="http://www.liferay.com/events/liferay-symposiums/west-coast-2011"&gt;West Coast Symposium&lt;/a&gt; in Anaheim on 21-22 of September.&lt;/p&gt;
&lt;p&gt;
	I hope to hear your thoughs on this and maybe to discuss it with you at WCS.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-09-03T18:05:47Z</dc:date>
  </entry>
  <entry>
    <title>Theme Settings and New Advanced Controls</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/theme-settings-and-new-advanced-controls" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/theme-settings-and-new-advanced-controls</id>
    <updated>2011-08-31T17:53:55Z</updated>
    <published>2011-08-26T02:58:44Z</published>
    <summary type="html">&lt;p&gt;
	To continue a recent string of theme related posts, I'm going to add another on the recently added feature allowing addition of advanced input controls and behaviors to theme settings.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;Themes&lt;/strong&gt; are the whip cream that bring flavour and sweetness to our portal fruit salads. Without beautiful and clever theme designs our fruit salad may taste healthy but can be bland and leave us wanting other treats.&lt;/p&gt;
&lt;p&gt;
	To make sure this doesn't happen, Liferay tries its hardest, each generation, to bring new and/or improved features and richness to the theme APIs in order to empower designers to create master pieces.&lt;/p&gt;
&lt;p&gt;
	One of the tools in the deisgners chest which was added in 6.0 and which added pretty significant flexibility to theme developers was the &lt;strong&gt;Settings API&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;
	As the name implies, the &lt;strong&gt;Settings API&lt;/strong&gt; is a mechanism that allows a designer to add configuration settings to their theme creations. These settings can then be used within the theme logic to do things like toggle features on or off, provide lists of options for display behaviors, provide boiler plate content fragments, etc. Using this API the designer can, with a single theme, attempt to provide a mixture of options that can potentially meet many more requirements and appeal to more users.&lt;/p&gt;
&lt;h2&gt;
	Using the Settings API&lt;/h2&gt;
&lt;p&gt;
	In order to use the settings API the designer must include a &lt;code&gt;liferay-look-and-feel.xml&lt;/code&gt; file in their theme. The path should be:&lt;/p&gt;
&lt;p&gt;
	&lt;code&gt;&amp;lt;theme&amp;gt;/docroot/WEB-INF/liferay-look-and-feel.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;
	As you might have guessed this is an xml file and a boiler plate file for 6.1 could be as trim as the following:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?xml version="1.0"?&amp;gt;
 &amp;lt;!DOCTYPE
&amp;nbsp; &amp;nbsp; look-and-feel&amp;nbsp;PUBLIC
&amp;nbsp; &amp;nbsp; "-//Liferay//DTD Look and Feel 6.1.0//EN"
&amp;nbsp; &amp;nbsp; "http://www.liferay.com/dtd/liferay-look-and-feel_6_1_0.dtd"&amp;gt;

&amp;lt;look-and-feel&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;compatibility&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;version&amp;gt;6.1.0+&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/compatibility&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;theme id="hotel" name="Hotel Theme" /&amp;gt;
&amp;lt;/look-and-feel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	Plainly enough this file defines the&amp;nbsp;compatibility the&amp;nbsp;theme has with different portal versions (in this case, since we're using features from 6.1 DTD, we're limiting our compatibility to 6.1+). It defines the theme element which contains the id of the theme as well as the name that will appear in the UI during theme selection/configuration.&lt;/p&gt;
&lt;p&gt;
	In order to add settings we're going to add an element as a child of the &lt;code&gt;&amp;lt;theme&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?xml version="1.0"?&amp;gt;
 &amp;lt;!DOCTYPE
&amp;nbsp; &amp;nbsp; look-and-feel&amp;nbsp;PUBLIC
&amp;nbsp; &amp;nbsp; "-//Liferay//DTD Look and Feel 6.1.0//EN"
&amp;nbsp; &amp;nbsp; "http://www.liferay.com/dtd/liferay-look-and-feel_6_1_0.dtd"&amp;gt;

&amp;lt;look-and-feel&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;compatibility&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;version&amp;gt;6.1.0+&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/compatibility&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;theme id="hotel" name="Hotel Theme" &amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;strong&gt;&amp;lt;settings&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/settings&amp;gt;&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/theme&amp;gt;
&amp;lt;/look-and-feel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	Each setting is defined using a single &lt;code&gt;&amp;lt;setting&amp;gt;&lt;/code&gt; elements with two main attributes, &lt;strong&gt;key&lt;/strong&gt; (the name by which the setting is handled) and &lt;strong&gt;value&lt;/strong&gt; (in the case of configurable setting, this will be the default value).&lt;/p&gt;
&lt;p&gt;
	e.g.&lt;/p&gt;
&lt;pre&gt;
...
&amp;lt;settings&amp;gt;
&amp;nbsp; &amp;nbsp;&lt;strong&gt;&amp;lt;setting key="show-breadcrumb" value="true" /&amp;gt;&lt;/strong&gt;
&amp;lt;/settings&amp;gt;
...
&lt;/pre&gt;
&lt;p&gt;
	There are three optional attributes (added in 6.1):&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		&lt;strong&gt;configurable&lt;/strong&gt; (whether or not the setting is static or editable from the UI)&lt;/li&gt;
	&lt;li&gt;
		&lt;strong&gt;type&lt;/strong&gt; (the data type for the field: checkbox, select, text, or textarea, default is text if not provided)&lt;/li&gt;
	&lt;li&gt;
		&lt;strong&gt;options&lt;/strong&gt; (in the case of type="select", options should contain a comma delimited list of values available to the selection)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	Therefore a slightly more advanced setting might be:&lt;/p&gt;
&lt;pre&gt;
...
&amp;lt;settings&amp;gt;
&lt;strong&gt;&amp;nbsp; &amp;nbsp; &amp;lt;setting&amp;nbsp;configurable="true"&amp;nbsp;key="show-breadcrumb"&amp;nbsp;type="checkbox"&amp;nbsp;value="true"&amp;nbsp;/&amp;gt;&lt;/strong&gt;
&amp;nbsp;&amp;lt;/settings&amp;gt; 
...
&lt;/pre&gt;
&lt;p&gt;
	So, how would this be used from the theme?&lt;/p&gt;
&lt;p&gt;
	The usage is pretty simple and is handled by a single method on the &lt;code&gt;ThemeDisplay&lt;/code&gt; object;&amp;nbsp;&lt;code&gt;getThemeSetting(String key)&lt;/code&gt;. The&amp;nbsp;&lt;code&gt;ThemeDisplay&lt;/code&gt; object you say? Do not fear, this object is already in the context of both Velocity and Freemarker. In Velocity it is available as both &lt;code&gt;$themeDisplay&lt;/code&gt;, as well as &lt;code&gt;$theme_display&lt;/code&gt;. In Freemaker it's available as &lt;code&gt;themeDisplay&lt;/code&gt; as well as &lt;code&gt;theme_display&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;
	Using the setting we created above, in your theme's portal_normal.vm (in a Velocity theme) you might do the following:&lt;/p&gt;
&lt;pre&gt;
#set ($show_breadcrumb = &lt;strong&gt;$theme_display.getThemeSetting('show-breadcrumb')&lt;/strong&gt;)
 
#if ($show_breadcrumb == 'true')
&amp;nbsp; &amp;nbsp; &amp;lt;nav class="site-breadcrumbs" id="breadcrumbs"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span&amp;gt;#language("breadcrumbs")&amp;lt;/span&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/h1&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #breadcrumbs()
&amp;nbsp; &amp;nbsp; &amp;lt;/nav&amp;gt;
#end
&lt;/pre&gt;
&lt;p&gt;
	Checkbox type fields have string values of either "true" or "false".&lt;/p&gt;
&lt;h2&gt;
	Advanced controls&lt;/h2&gt;
&lt;p&gt;
	So far I have not talked about the&amp;nbsp;&lt;strong&gt;Advanced Controls&lt;/strong&gt; I referred to in the title of the post.&lt;/p&gt;
&lt;p&gt;
	So you have controls, fine! Checkboxs, text fields, textareas, and selects are great but we can't exactly call this a great collection of "rich" controls. These are the lowest common controls that you'd probably expect. We're talking about theme developement here, so we want some POP and PIZZAZ (yes PIZZAZ!!!).&lt;/p&gt;
&lt;p&gt;
	To solve this limitation without trying to provide for every scenario imaginable we added the ability for a setting definition to contain a block of javascript code which would be inlined immediately with the input field. This javascript would allow the designer to take the basic field types and turn them into a more advanced control.&lt;/p&gt;
&lt;p&gt;
	For the following example I'm going to bind a color picker tool to a regular text field.&lt;/p&gt;
&lt;p&gt;
	First the base xml for the definition:&lt;/p&gt;
&lt;pre&gt;
...
&amp;lt;setting&amp;nbsp;
&amp;nbsp; &amp;nbsp; configurable="true"
&amp;nbsp; &amp;nbsp; &lt;strong&gt;key="portal-main-color"&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &lt;strong&gt;type="text"&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &lt;strong&gt;value="#EEF0F2"&amp;gt;&lt;/strong&gt;
&amp;lt;![CDATA[
]]&amp;gt;
&amp;lt;/setting&amp;gt;
...
&lt;/pre&gt;
&lt;p&gt;
	Now, I'm going to use the Alloy UI&amp;nbsp;color-picker component and some other simple code for making the field read-only so users can't input faulty data:&lt;/p&gt;
&lt;pre&gt;
...
&amp;lt;setting&amp;nbsp;
&amp;nbsp; &amp;nbsp; configurable="true"
&amp;nbsp; &amp;nbsp; &lt;strong&gt;key="portal-main-color"&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &lt;strong&gt;type="text"&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &lt;strong&gt;value="#EEF0F2"&amp;gt;&lt;/strong&gt;
&amp;lt;![CDATA[&amp;nbsp;
&lt;/pre&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; AUI().ready(&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'aui-color-picker-base',&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; function(A) {&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var target = A.one('&lt;strong&gt;#[@NAMESPACE@]portal-main-color&lt;/strong&gt;');&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; target.attr('readonly', 'readonly');&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var currentValue = target.val();&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; target.ancestor(&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; '.aui-field-element').append( &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "&amp;lt;div id='[@NAMESPACE@]PortalMainColorPicker'&amp;gt;&amp;lt;/div&amp;gt;");&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(function() {&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var [@NAMESPACE@]PortalMainColorPicker = new A.ColorPicker(&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; hex: currentValue&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ).render('#[@NAMESPACE@]PortalMainColorPicker');&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [@NAMESPACE@]PortalMainColorPicker.on( &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'colorChange',&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; function(event) {&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var hex = [@NAMESPACE@]PortalMainColorPicker.get("hex");&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; target.val('#' + hex);&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }, 0);&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;
	&amp;nbsp; &amp;nbsp; );&lt;/div&gt;
&lt;div&gt;
	]]&amp;gt;&amp;nbsp;&lt;/div&gt;
&lt;p&gt;
	&amp;lt;/setting&amp;gt; ...&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	If you read through the javascript you will see that we get an element using the A.one() alloy call. This is akin to document.getElementById() but it works by using css selectors. In this case we're getting the setting field by using the &lt;strong&gt;key&lt;/strong&gt; of the field prefixed by the # symbol, which in CSS means that it's the ID of an element. The key is also prefixed by the token&lt;code&gt;&amp;nbsp;[@NAMESPACE@]&lt;/code&gt;. This token is used to avoid namespace collisions with other applications that may be floating around the page and is replaced at runtime with the actual namespace of the theme configuration app (in a portal we always need to be conscious that we may be co-existing with other apps). Use the token whenever creating public variables or dom objects.&lt;/p&gt;
&lt;p&gt;
	Once we have the field, we can manipulate it however we like.&lt;/p&gt;
&lt;h2&gt;
	The Advanced Control Result&lt;/h2&gt;
&lt;p&gt;
	We've added the control, so what does it look like?&lt;/p&gt;
&lt;p&gt;
	Here is the settings panel with the new field and the control:&lt;/p&gt;
&lt;p&gt;
	&lt;img alt="" src="http://gyazo.com/f54f0bfd9002f1063ea6a1c01b0aebf1.png" /&gt;&lt;/p&gt;
&lt;p&gt;
	With the control active:&lt;/p&gt;
&lt;p&gt;
	&lt;img alt="" src="http://gyazo.com/60dd2c48b26ff2b954c6522c344c6827.png" /&gt;&lt;/p&gt;
&lt;p&gt;
	Moving the cursor around the color-picker while left button pressing on the mouse will move the color-picker cursor and update the value in the setting text field until you release the button.&lt;/p&gt;
&lt;h2&gt;
	The Possibilities&lt;/h2&gt;
&lt;p&gt;
	Given the multitude of advanced controls we can create using javascript and the simple usage of the Settings API, the possibilities are quite limitless.&lt;/p&gt;
&lt;p&gt;
	I hope that this takes your theme development to a whole new level of creativity and richness.&lt;/p&gt;
&lt;p&gt;
	I'll be at &lt;a href="http://www.liferay.com/events/liferay-symposiums/west-coast-2011"&gt;WCS&lt;/a&gt; doing a couple of talks and a workshop which I'll be talking about shortly. So if you have questions regarding any of the things I've writen about recently, or about any other topic actually, and you're attending WCS, please bring those along with you.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;See you at WCS!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;Update: &lt;/strong&gt;&lt;/span&gt;I wanted to add that the Theme Settings feature was originally implemented and donated to Liferay as part of a community contribution by&amp;nbsp;&lt;b&gt;José Ignacio Huertas (&lt;/b&gt;&lt;a href="http://issues.liferay.com/browse/LPS-12468" target="_blank"&gt;LPS-12468&lt;/a&gt;&lt;b&gt;)&lt;/b&gt;. I'd like to thank&amp;nbsp;José&amp;nbsp;for his contribution. This is a very good example of how a small contribution by the community can become a very cool feature. I would like to encourage others to participate by donating their feature implementations, suggestions, bug reports, documentation skills, or translation skills. This is the key way that Liferay has become the rich platform that it is.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-08-26T02:58:44Z</dc:date>
  </entry>
  <entry>
    <title>Theme JSP Overrides</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/theme-jsp-overrides" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/theme-jsp-overrides</id>
    <updated>2011-08-22T20:23:27Z</updated>
    <published>2011-08-22T19:24:38Z</published>
    <summary type="html">&lt;p&gt;
	Ok, so in the &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/jsp-include-buffer"&gt;last post&lt;/a&gt; I talked about &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/jsp-include-buffer"&gt;JSP Include Buffer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	That post talked about how you'd override JSPs from Liferay's core in a maintainable way. Now I'd like to introduce you to a Liferay Theme feature, new in 6.1, that will grant you much more power when it comes to manipulating portal views from your theme using that plus this additional feature.&lt;/p&gt;
&lt;p&gt;
	From your theme you can now include templates (following the same path maning as they are found in the core) in your themes which override those of the core, provided they have a template extension matching that of your themes. They will override the default view of the portatl &lt;strong&gt;but only where and when your theme is applied&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;
	Now think about this! When you apply a theme onto any page of your portal, &lt;em&gt;you can alter the view of any portlet on just those pages!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
	Let me illustrate with an example. I'll use the exact same example that was intriduced in the previous post just so it easier to follow along.&lt;/p&gt;
&lt;p&gt;
	Assuming for the moment that our theme is a &lt;strong&gt;Freemarker&lt;/strong&gt; theme, we add the following template to our theme:&lt;/p&gt;
&lt;p&gt;
	&lt;code&gt;&amp;lt;SDK_ROOT&amp;gt;/themes/my-theme/docroot/_diffs/templates&lt;span style="font-family: monospace; "&gt;/html/taglib/ui/page_iterator&lt;/span&gt;/start.ftl&lt;br /&gt;
	&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;
	In this template we place the following code (using the buffer pattern):&lt;/p&gt;
&lt;pre&gt;
&amp;lt;#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] /&amp;gt;

&amp;lt;@liferay_util["buffer"] var="html"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;@liferay_util["include"] 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; page="&lt;span style="color:#00cc00;"&gt;&lt;strong&gt;/html/taglib/ui/page_iterator/start.jsp&lt;/strong&gt;&lt;/span&gt;" 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color:#ff0000;"&gt;&lt;strong&gt;strict=true&lt;/strong&gt;&lt;/span&gt; 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; useCustomPage=false 
&amp;nbsp; &amp;nbsp; /&amp;gt; 
&amp;lt;/@&amp;gt; 
 
&amp;lt;div style="border: 4px solid &lt;strong&gt;red&lt;/strong&gt;; padding: 4px;"&amp;gt;
&amp;nbsp; &amp;nbsp; ${html}
&amp;lt;/div&amp;gt;&lt;/pre&gt;
&lt;p&gt;
	So, you should note two things.&lt;/p&gt;
&lt;p&gt;
	First, unlike in the previous post I don't have to use the &lt;code&gt;.portal&lt;/code&gt; encoded name for the jsp because it's still where it started, the portal didn't relocate it since my override is in the theme (it only does that when using hook plugins).&lt;/p&gt;
&lt;p&gt;
	Second, the attribute &lt;code&gt;strict=true&lt;/code&gt;. As I mentioned before, but will again, this is to prevent the portal from doing a lookup for overrides when making the call to the underlying JSP (this is to avoid infinite recursion since we're in an override right now).&lt;/p&gt;
&lt;p&gt;
	Ok, I can noew enhance or alter this view no problem without having to reproduce the whole of the original logic and I'm doing it from my theme no less!&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;What more could you ask for?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	Well it turns out there IS more that could be asked and since &lt;em&gt;it was asked&amp;nbsp;for,&lt;/em&gt; we made it reality (that's what we do at Liferay, we make wishes come true).&lt;/p&gt;
&lt;p&gt;
	The question was "&lt;strong&gt;Could you override a JSP, but only for a particular portlet?&lt;/strong&gt;"&lt;/p&gt;
&lt;p&gt;
	Since overriding a blog JSP only in blogs doesn't really make sense, nor does overriding a blog JSP only for message boards, the main case here is with taglib JSPs. Liferay has a vast number of tags and their view logic are largely implemented with JSPs.&lt;/p&gt;
&lt;p&gt;
	So, the answer to that is, "&lt;strong&gt;Yes, now you can override a JSP in your theme for only specific portlets!&lt;/strong&gt;"&lt;/p&gt;
&lt;p&gt;
	How do you do it?&lt;/p&gt;
&lt;p&gt;
	Simple! All you need to do is add the portlet Id of the target portlet, between the file name of the original JSP and the extension of the template like so:&lt;/p&gt;
&lt;p&gt;
	&lt;code&gt;&amp;lt;SDK_ROOT&amp;gt;/themes/my-theme/docroot/_diffs/templates/html/taglib/ui/page_iterator/start&lt;strong&gt;.19&lt;/strong&gt;.ftl&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;
	Using a similar template containing (note the green border instead):&lt;/p&gt;
&lt;pre&gt;
&amp;lt;#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] /&amp;gt;

&amp;lt;@liferay_util["buffer"] var="html"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;@liferay_util["include"]
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; page="&lt;span style="color: rgb(0, 204, 0); "&gt;&lt;strong&gt;/html/taglib/ui/page_iterator/start.jsp&lt;/strong&gt;&lt;/span&gt;"
&lt;span style="color: rgb(255, 0, 0); "&gt;&lt;strong&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; strict=true&lt;/strong&gt;&lt;/span&gt; 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; useCustomPage=false 
&amp;nbsp; &amp;nbsp; /&amp;gt;
&amp;lt;/@&amp;gt;

&amp;lt;div style="border: 4px solid &lt;strong&gt;green&lt;/strong&gt;; padding: 4px;"&amp;gt; 
&amp;nbsp; &amp;nbsp; ${html} 
&amp;lt;/div&amp;gt; &amp;nbsp;
&lt;/pre&gt;
&lt;p&gt;
	Now, you'll note that we have two new tempalate files in our theme:&lt;/p&gt;
&lt;pre&gt;
_diffs/templates/html/taglib/ui/page_iterator/start.ftl
_diffs/templates/html/taglib/ui/page_iterator/start.19.ftl &amp;nbsp;&lt;/pre&gt;
&lt;p&gt;
	These two files do not conflict, and the precendence will be:&lt;/p&gt;
&lt;p&gt;
	1. portlet specific template&lt;br /&gt;
	2. non portlet specific template&lt;br /&gt;
	3. (no file) default&lt;/p&gt;
&lt;p&gt;
	If your template is trying to override the JSP for a portlet that is in a plugin, make sure to use the fully qualified portletId (which is encoded using the pattern&amp;nbsp;&lt;code&gt;portletid_WAR_plugincontextname&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;
	Now if you place two portlets on the page while this theme is in effect on it, and each of those portlets use the &lt;code&gt;liferay-util:page-iterator&lt;/code&gt; tag, and one of those portlets is the Message Boards Portlet (19) then you should see both red and green boxes around those parts of the page.&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	Oh, and remember that &lt;strong&gt;you CAN do the same from a Velocity theme&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;
_diffs/templates/html/taglib/ui/page_iterator/start.vm
_diffs/templates/html/taglib/ui/page_iterator/start.19.vm&lt;/pre&gt;
&lt;p&gt;
	The template (using the buffer pattern) would look more like this:&lt;/p&gt;
&lt;pre&gt;
#set ($bufferTagClass = $portal.class.forName("com.liferay.taglib.util.BufferTag"))
#set ($includeTagClass = $portal.class.forName("com.liferay.taglib.util.IncludeTag"))
 
#set ($bufferTag = $bufferTagClass.newInstance())
#set ($V = $bufferTag.setPageContext($pageContext))
#set ($V = $bufferTag.setParent(null))
#set ($V = $bufferTag.setVar('html'))
 
#if ($bufferTag.doStartTag() == 2)
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.setBodyContent($pageContext.pushBody()))
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doInitBody())
&amp;nbsp; &amp;nbsp; #set ($includeTag = $includeTagClass.newInstance())
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setPageContext($pageContext))

&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setPage('/html/taglib/ui/page_iterator/start.jsp'))

&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setStrict(true))
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setUseCustomPage(false))
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.runTag())
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doAfterBody())
&amp;nbsp; &amp;nbsp; #set ($V = $pageContext.popBody())
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doEndTag())
#end
 
&amp;lt;div style="border: 4px solid red; padding: 4px;"&amp;gt;
&amp;nbsp; &amp;nbsp; ${pageContext.findAttribute('html')}
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	Now, I hope you find more uses for these features that colourful boxes. Good luck!&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-08-22T19:24:38Z</dc:date>
  </entry>
  <entry>
    <title>JSP Include Buffer</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/jsp-include-buffer" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/jsp-include-buffer</id>
    <updated>2011-08-22T15:08:53Z</updated>
    <published>2011-08-22T13:54:37Z</published>
    <summary type="html">&lt;p&gt;
	There are plenty of times when you might want to alter the default views of the portal. I'm not going to go into the &lt;em&gt;&lt;strong&gt;why&lt;/strong&gt;&lt;/em&gt; since that is hugely subjective. What I do want to show is the &lt;strong&gt;&lt;em&gt;how&lt;/em&gt;&lt;/strong&gt; you can do this in the most maintainable way possible.&lt;/p&gt;
&lt;p&gt;
	The largest concern with core JSP modification in projects is of course maintainability. JSPs have no "extensability" feature of their own and so when taking modified core JSPs into your project you run the risk of causing yourself a tone of heartache during future upgrades.&lt;/p&gt;
&lt;p&gt;
	JSPs don't have any contract that allows for cleanly identifying when something has changed or gone wrong between versions.&amp;nbsp;At least with pure source code you can take a leap and just try compiling against the latest version and although you likely run into many errors, they are quite literally spelled out for you. With JSPs you don't have this luxery and it's quite possible to go weeks or months with JSPs that seem to work after an upgrade but that suddenly start to exhibit bad behavior as your system starts to hit those edge cases or outright fail for the same reason. This is typically because of some unforseen change in the core logic.&lt;/p&gt;
&lt;p&gt;
	Developers often try to mitigate this by doing things like using some complex or just "obvious" comments explaining changed code, hoping they are still around to review the changes on future upgrades...&lt;/p&gt;
&lt;p&gt;
	What's likely to happen is that there ends up being so many JSPs changed and/or that the same developer is not around for the next upgrade, a complete and thorough review of those changes simply doesn't get done, risks are run and the ensuing issues become costly.&lt;/p&gt;
&lt;p&gt;
	So goes the story of "JSP Hell"!&lt;/p&gt;
&lt;p&gt;
	Meanwhile JSPs are still the best performing RAD view techology in java, so they are still used quite heavily.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;What to do?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	Liferay has long experienced this scenario and though we knew that changing from JSP was either unlikely or unreasonable considering the alternatives, we still had to come up with a good solution to the problem.&lt;/p&gt;
&lt;p&gt;
	The solution is quite simple, the&amp;nbsp;&lt;strong&gt;JSP Include Buffer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;
	The basic principle is to simply buffer, then extend the result that is output from the original JSP. Thus we can change&amp;nbsp;only the parts we care about, and latter if there is a change in the original, it will either cause our extension to fail, or it will be seamlessly integrated, exactly the way a source code change would (even though it won't be a build time error, it will still be far more evident than the previous alternative).&lt;/p&gt;
&lt;p&gt;
	There is one limitation, the JSP that you want to perform this operation on must NOT be a jsp fragment (a fragement is a JSP that you include using the low level&amp;nbsp;&lt;code&gt;&amp;lt;%@ include file="****.jsp" %&amp;gt;&lt;/code&gt; directive.&lt;/p&gt;
&lt;p&gt;
	Rather it should be one using an include tag, such as &lt;code&gt;&amp;lt;jsp:include /&amp;gt;&lt;/code&gt; or in the case of Liferay &lt;code&gt;&amp;lt;liferay-util:include /&amp;gt;&lt;/code&gt;, i.e. one that is invoked through a request dispatcher.&lt;/p&gt;
&lt;p&gt;
	The examples I'm going to show will be using the &lt;code&gt;&amp;lt;liferay-uitl:include /&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;From a&amp;nbsp;JSP Hook&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
	Liferay offers a plugin type called a "Hook" that allows you to define JSPs that should &lt;strong&gt;override&lt;/strong&gt; (not &lt;strong&gt;overwrite&lt;/strong&gt;) JSPs in the core, and on deploy these jsps replace those in the core. BUT the core JSPs are not lost (remember &lt;strong&gt;override&lt;/strong&gt; not &lt;strong&gt;overwrite&lt;/strong&gt;). The core JSPs are simply re-located, thus they are still accessible.&lt;/p&gt;
&lt;p&gt;
	The core JSPs are relocated to a new name which injects the keyword &lt;code&gt;.portal&lt;/code&gt; between the file name and extension. Thus a core JSP having the name &lt;code&gt;start.jsp&lt;/code&gt; after being "Hooked" would be located under the name&amp;nbsp;&lt;code&gt;start.portal.jsp&lt;/code&gt; (at the exact same path).&lt;/p&gt;
&lt;p&gt;
	How does this help us with the above problem?&lt;/p&gt;
&lt;p&gt;
	Simply it means that we have the original to work with and so we don't need to completely re-implement or reproduce the complete code in our "extended" implementation. We can still call the original, buffer it's output and then manipulate the result to our needs.&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:12px;"&gt;Here is a very simple example or wrapping the output of the original with a simple black box (you could do this via CSS but what's the fun in that, and this example could serve many more uses that simple css styling can ever achieve).&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&amp;lt;%@ taglib uri="&lt;a href="http://liferay.com/tld/util" style="color:#3465A4"&gt;http://liferay.com/tld/util&lt;/a&gt;" prefix="liferay-util" %&amp;gt;

&lt;span style="font-family:monospace"&gt;&amp;lt;liferay-util:buffer var="html"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;liferay-util:include
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; page="&lt;span style="font-family:monospace"&gt;/html/taglib/ui/page_iterator&lt;/span&gt;/start.portal.jsp"
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; useCustomPage="&amp;lt;%= false %&amp;gt;"
&amp;nbsp; &amp;nbsp; /&amp;gt;
&amp;lt;/liferay-util:buffer&amp;gt;
&lt;/span&gt;&lt;span style="font-family:monospace"&gt;
&amp;lt;div style="border: 4px solid red; padding: 4px;"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;%= html %&amp;gt;
&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;From a FreeMarker Template&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;span style="font-size:12px;"&gt;Can the same be achieved from Freemarker you ask? Certainly! And here is the same example as above.&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;span style="font-size:12px;"&gt; &lt;/span&gt;&amp;lt;#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] /&amp;gt;

&amp;lt;@liferay_util["buffer"] var="html"&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;@liferay_util["include"]
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; page="/html/taglib/ui/page_iterator/start.jsp"
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;strong&gt;strict=true&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style="font-family:monospace"&gt;useCustomPage=false
&amp;nbsp; &amp;nbsp; /&amp;gt;
&amp;lt;/@&amp;gt;

&amp;lt;div style="border: 4px solid red; padding: 4px;"&amp;gt;
&amp;nbsp; &amp;nbsp; ${html}
&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
	&lt;span style="font-size:14px;"&gt;&lt;strong&gt;From a Velocity Template&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
	Hold up! You're kidding right? Velocity can't do jsp tags...&lt;/p&gt;
&lt;p&gt;
	Well it can! It's just not pretty. Why would you do this from a Velocity anyway? Well, you never know what people want to do in their themes and the next blog post I write about how to override JSPs from theme templates will make this here feature all the more interesting for theme developers, AND by supporting Velocity, won't force all those developers to convert their existing themes to Freemarker just so they can override the odd JSP.&lt;/p&gt;
&lt;p&gt;
	So, here goes (remember I don't promise that this is beautiful, only that it works and works well).&lt;/p&gt;
&lt;pre&gt;
&lt;span style="font-family:monospace"&gt;#set ($bufferTagClass = $portal.class.forName("com.liferay.taglib.util.BufferTag"))
#set ($includeTagClass = $portal.class.forName("com.liferay.taglib.util.IncludeTag"))

#set ($bufferTag = $bufferTagClass.newInstance())
#set ($V = $bufferTag.setPageContext($pageContext))
#set ($V = $bufferTag.setParent(null))
#set ($V = $bufferTag.setVar('html'))

#if ($bufferTag.doStartTag() == 2)
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.setBodyContent($pageContext.pushBody()))
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doInitBody())

&amp;nbsp; &amp;nbsp; #set ($includeTag = $includeTagClass.newInstance())
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setPageContext($pageContext))

&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setPage('/html/taglib/ui/page_iterator/start.jsp'))

&amp;nbsp; &amp;nbsp; &lt;strong&gt;#set ($V = $includeTag.setStrict(true))&lt;/strong&gt;
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.setUseCustomPage(false))
&amp;nbsp; &amp;nbsp; #set ($V = $includeTag.runTag())
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doAfterBody())
&amp;nbsp; &amp;nbsp; #set ($V = $pageContext.popBody())
&amp;nbsp; &amp;nbsp; #set ($V = $bufferTag.doEndTag())
#end

&amp;lt;div style="border: 4px solid red; padding: 4px;"&amp;gt;
&amp;nbsp; &amp;nbsp; ${pageContext.findAttribute('html')}
&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
	And there you have it!&lt;/p&gt;
&lt;p&gt;
	Now you'll notice that I bolded a coupled of lines above in the template examples. These lines, which set the field &lt;code&gt;strict&lt;/code&gt; on the include tag to &lt;code&gt;true&lt;/code&gt;, are &lt;strong&gt;VERY&lt;/strong&gt; important. They tell the underlying tag &lt;strong&gt;NOT&lt;/strong&gt;&amp;nbsp;check if there are overrides for the specified JSP include (otherwise you may end up with infinite recursion and likely stack overflow errors ;) ).&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	I hope this can help some of you to biuld more maintainable solutions on Liferay with less fear of the upgrade process.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-08-22T13:54:37Z</dc:date>
  </entry>
  <entry>
    <title>Opening a portlet in dialog from web content</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/opening-a-portlet-in-dialog-from-web-content" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/opening-a-portlet-in-dialog-from-web-content</id>
    <updated>2011-08-12T14:51:36Z</updated>
    <published>2011-08-11T21:58:26Z</published>
    <summary type="html">&lt;p&gt;
	Recently I was asked how to do this! Well, it's rather simple (providing you understand the limitations of accessing portlets without permission, and portlets that are not allowed to be loaded dynamically; a.k.a. add-default-resource, and so on).&lt;/p&gt;
&lt;p&gt;
	Here goes!&lt;/p&gt;
&lt;p&gt;
	First I created a simple web content structure/type with the following definition:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;root&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;dynamic-element name='portlet-id' type='text' index-type='' repeatable='false'&amp;gt;&amp;lt;/dynamic-element&amp;gt; &amp;nbsp;
&amp;nbsp; &amp;nbsp; &amp;lt;dynamic-element name='parameters' type='text' index-type='' repeatable='true'&amp;gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;dynamic-element name='value' type='text' index-type='' repeatable='false'&amp;gt;&amp;lt;/dynamic-element&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/dynamic-element&amp;gt;
&amp;lt;/root&amp;gt;&lt;/pre&gt;
&lt;p&gt;
	Then I created the following web content template:&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;
&amp;lt;script type="text/javascript" charset="utf-8"&amp;gt;
&amp;nbsp; &amp;nbsp; AUI().ready('aui-dialog','aui-dialog-iframe','liferay-portlet-url', function(A) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var url = Liferay.PortletURL.createRenderURL();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url.setPortletId("${portlet-id.data}");
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url.setWindowState('pop_up'); 

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #foreach ($parameter IN $parameters.getSiblings())
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url.setParameter("${parameter.data}", "${parameter.value.data}");
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #end &amp;nbsp;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; window.myDialog = new A.Dialog(
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title: 'My Dialog',
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 640,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; centered: true
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ).plug(
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; A.Plugin.DialogIframe,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; uri: url.toString(),
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; iframeCssClass: 'dialog-iframe'
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ).render();
&amp;nbsp; &amp;nbsp; });
 &amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	As you can see there is a place for setting the portletId to craete an URL for, as well as a repeated section for assigning parameters to the url.&lt;/p&gt;
&lt;p&gt;
	Creating an article with the Portlet Id value of 3 results in a dialog opening with the search portlet.&lt;/p&gt;
&lt;p&gt;
	&lt;img alt="" src="http://gyazo.com/8913ccc6b90c440a058f3b7e78a4eb21.png" style="width: 668px; height: 131px; " /&gt;&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	You can expand this simple example into whatever you like. The Alloy Dialog plugin is very rich and the Liferay.PortletURL library provides a complete API for creating all of the PortletURL types defined by the portlet spec.&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	Enjoy!&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-08-11T21:58:26Z</dc:date>
  </entry>
  <entry>
    <title>Secure RSS Feeds</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/secure-rss-feeds" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/secure-rss-feeds</id>
    <updated>2011-02-17T14:10:40Z</updated>
    <published>2011-02-16T20:52:31Z</published>
    <summary type="html">&lt;p&gt;All secure RSS feeds now transparently support BASIC&amp;nbsp; &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;Authentication.  &lt;/meta&gt;&lt;/p&gt; &lt;p&gt;The behavior is such that when you're logged in, the feeds will simply work as expected if you open them directly in the browser. If you log out, you'll suddenly be promted for BASIC&amp;nbsp; &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;Authentication. If you use the url with an external RSS client and that client supports BASIC Authentication then simply give your credentials and you should be good to go.  &lt;/meta&gt;&lt;/p&gt; &lt;p&gt;Pleae note that BASIC&amp;nbsp;Authentication transfers your passwords over the internet/network in plain text, so make sure that you have SSL enabled if you care about such things.&lt;/p&gt; &lt;p&gt;An alternative is also to enabled DIGEST&amp;nbsp;Authentication (just add an init parameter for &amp;quot;digest_auth&amp;quot; in the filter declaration and restart).&lt;/p&gt; &lt;p&gt;Note of you're using an SSO of some kind, and the client making the request speaks in your SSO's toung, then of course&amp;nbsp;you don't have to worry about any of this.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Enjoy!&lt;/p&gt; &lt;p&gt;(&lt;strong&gt;Update:&lt;/strong&gt;&amp;nbsp;The issue was resolved as of &lt;a href="http://issues.liferay.com/browse/LPS-12308"&gt;LPS-12308&lt;/a&gt;&amp;nbsp;r73243.)&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-02-16T20:52:31Z</dc:date>
  </entry>
  <entry>
    <title>Advanced Web Content Example with AJAX</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/advanced-web-content-example-with-ajax" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/advanced-web-content-example-with-ajax</id>
    <updated>2011-02-04T23:33:01Z</updated>
    <published>2011-02-02T19:05:16Z</published>
    <summary type="html">&lt;div&gt;&lt;p&gt;This example demonstrates several advanced features of Liferay's Web Content Management provided when taking full advantage of Liferay's Web Content Template processing engine. It'll demonstrate implementing PHASES of the portlet lifecycle, performing AJAX calls, using Alloy Javascript Library, using Liferay's SearchEngine, converting java objects to JSON for passing back as AJAX response body all from within our templates.&lt;/p&gt; &lt;p&gt;It starts with a structured piece of content we'll call a Widget, defined by the following Web Content Structure:&lt;/p&gt; &lt;pre&gt;
&amp;lt;root&amp;gt;
  &amp;lt;dynamic-element name='name' type='text' index-type='text' repeatable='false'&amp;gt;
  &amp;lt;meta-data&amp;gt;
			&amp;lt;entry name=&amp;quot;displayAsTooltip&amp;quot;&amp;gt;&amp;lt;![CDATA[true]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;required&amp;quot;&amp;gt;&amp;lt;![CDATA[false]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;instructions&amp;quot;&amp;gt;&amp;lt;![CDATA[Enter plain text only.]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;label&amp;quot;&amp;gt;&amp;lt;![CDATA[Widget Name]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;predefinedValue&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
		&amp;lt;/meta-data&amp;gt;
&amp;lt;/dynamic-element&amp;gt;
  &amp;lt;dynamic-element name='description' type='text_box' index-type='text' repeatable='false'&amp;gt;
  &amp;lt;meta-data&amp;gt;
			&amp;lt;entry name=&amp;quot;displayAsTooltip&amp;quot;&amp;gt;&amp;lt;![CDATA[true]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;required&amp;quot;&amp;gt;&amp;lt;![CDATA[false]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;instructions&amp;quot;&amp;gt;&amp;lt;![CDATA[Prefer to use plain text here, at worst use only simple HTML tags like strong, em, etc.]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;label&amp;quot;&amp;gt;&amp;lt;![CDATA[Description]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;predefinedValue&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
		&amp;lt;/meta-data&amp;gt;
&amp;lt;/dynamic-element&amp;gt;
  &amp;lt;dynamic-element name='image' type='document_library' index-type='keyword' repeatable='false'&amp;gt;
  &amp;lt;meta-data&amp;gt;
			&amp;lt;entry name=&amp;quot;displayAsTooltip&amp;quot;&amp;gt;&amp;lt;![CDATA[true]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;required&amp;quot;&amp;gt;&amp;lt;![CDATA[false]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;instructions&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;label&amp;quot;&amp;gt;&amp;lt;![CDATA[Widget Image]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;predefinedValue&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
		&amp;lt;/meta-data&amp;gt;
&amp;lt;/dynamic-element&amp;gt;
  &amp;lt;dynamic-element name='document' type='document_library' index-type='keyword' repeatable='false'&amp;gt;
  &amp;lt;meta-data&amp;gt;
			&amp;lt;entry name=&amp;quot;displayAsTooltip&amp;quot;&amp;gt;&amp;lt;![CDATA[true]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;required&amp;quot;&amp;gt;&amp;lt;![CDATA[false]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;instructions&amp;quot;&amp;gt;&amp;lt;![CDATA[Link a PDF document preferably.]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;label&amp;quot;&amp;gt;&amp;lt;![CDATA[Widget Document]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;predefinedValue&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
		&amp;lt;/meta-data&amp;gt;
&amp;lt;/dynamic-element&amp;gt;
&amp;lt;/root&amp;gt;&lt;/pre&gt; &lt;p&gt;Make sure that each field of the structure is marked as &amp;quot;Searchable&amp;quot; as either text or token otherwise later on we won't be able to individually access those fields. The template for this structure might look like this (but it's not overly important for this example):&lt;/p&gt; &lt;pre&gt;
&amp;lt;h3&amp;gt;$name.data&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img style=&amp;quot;float: left;&amp;quot; src=&amp;quot;$image.data&amp;quot; alt=&amp;quot;$name.data&amp;quot;/&amp;gt; $description.data&amp;lt;/p&amp;gt;

&amp;lt;a href=&amp;quot;$document.data&amp;quot;&amp;gt;Spec Sheet&amp;lt;/a&amp;gt;&lt;/pre&gt; &lt;p&gt;Now given that we have a few pieces of content using this structure, our goal is to create an enhanced view  that leverage AJAX in order to be cool... er fullfill our business requirements!&lt;/p&gt; &lt;p&gt;So, we're going to create a structure to act as the settings schema for a simple Web Content app:&lt;/p&gt; &lt;pre&gt;
&amp;lt;root&amp;gt;
  &amp;lt;dynamic-element name='number-of-items' type='text' index-type='' repeatable='false'&amp;gt;
  &amp;lt;meta-data&amp;gt;
			&amp;lt;entry name=&amp;quot;displayAsTooltip&amp;quot;&amp;gt;&amp;lt;![CDATA[true]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;required&amp;quot;&amp;gt;&amp;lt;![CDATA[false]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;instructions&amp;quot;&amp;gt;&amp;lt;![CDATA[How many items to retrieve with ajax.]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;label&amp;quot;&amp;gt;&amp;lt;![CDATA[Number of items]]&amp;gt;&amp;lt;/entry&amp;gt;
			&amp;lt;entry name=&amp;quot;predefinedValue&amp;quot;&amp;gt;&amp;lt;![CDATA[]]&amp;gt;&amp;lt;/entry&amp;gt;
		&amp;lt;/meta-data&amp;gt;
&amp;lt;/dynamic-element&amp;gt;
&amp;lt;/root&amp;gt;&lt;/pre&gt; &lt;p&gt;Finally, we get to the good part which is the template of the Web Content app. I've implemented this example using Velocity just because it's the one I'm most comfortable with. The template implements both the front end logic as well as the backend that will handle our AJAX request. &lt;span class="b"&gt;Remember when writing templates that implement request handling to unckeck &amp;quot;Cacheable&amp;quot;.&lt;/span&gt;&lt;/p&gt; &lt;pre&gt;
#set ($ns = $request.portlet-namespace)
#set ($companyId = $getterUtil.getLong($request.theme-display.company-id))
#set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id))
#set ($numberOfItems = $getterUtil.getInteger($number-of-items.data))

#if ($request.lifecycle == 'RESOURCE_PHASE')

	&lt;span class="b"&gt;## This phase will handle the ajax request.&lt;/span&gt;

#else

	&lt;span class="b"&gt;## This phase (default is 'RENDER_PHASE') will handle the view.&lt;/span&gt;

#end&lt;/pre&gt; &lt;p&gt;Let's look at the view logic and the AJAX call that drives it! It uses Liferay's very own &lt;a href="http://alloy.liferay.com"&gt;Alloy Javascript Library&lt;/a&gt; to perform the AJAx call.&lt;/p&gt; &lt;pre&gt;
...
#else
	&amp;lt;table class=&amp;quot;taglib-search-iterator&amp;quot;&amp;gt;
		&amp;lt;thead&amp;gt;
			&amp;lt;tr class=&amp;quot;portlet-section-header results-header&amp;quot;&amp;gt;
				&amp;lt;th&amp;gt;
					Widgets
				&amp;lt;/th&amp;gt;
			&amp;lt;/tr&amp;gt;
		&amp;lt;/thead&amp;gt;
		&amp;lt;tbody class=&amp;quot;${ns}results-container&amp;quot;&amp;gt;
			&amp;lt;tr class=&amp;quot;portlet-section-body results-row last&amp;quot;&amp;gt;
				&amp;lt;td&amp;gt;
					No Widgets
				&amp;lt;/td&amp;gt;
			&amp;lt;/tr&amp;gt;
		&amp;lt;/tbody&amp;gt;
	&amp;lt;/table&amp;gt;

	&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
	AUI().use(
		'aui-base', 'aui-io',
		function(A) {
			var search = function(eventType) {
				A.io.request(
					'${request.resource-url}',
					{
						dataType: 'json',
						on: {
							success: function(event, id, obj) {
								var instance = this;

								var hits = instance.get('responseData');
								
								var resultsContainer = A.one('.${ns}results-container');
						
								if (!hits &amp;amp;&amp;amp; !hits.docs) {
									return;
								}

								resultsContainer.empty();
								
								for (var i = 0; i &amp;lt; hits.docs.length; i++) {
									var doc = hits.docs[i];

									console.log(doc);
								
									var title = doc.fields.map['web_content/name'].value || doc.fields.map.uid.value;
									var description = doc.fields.map['web_content/description'].value;
									var image = doc.fields.map['web_content/image'].value;
									var document = doc.fields.map['web_content/document'].value;
									
									var position = ' portlet-section-body';
									
									if (i % 2 == 1) {
										position = ' portlet-section-alternate alt';
									}
									
									if (i == 0) {
										position += ' first';
									}
									else if (i == hits.length - 1) {
										position += ' last';
									}
		
									resultsContainer.append(
										[
											'&amp;lt;tr class=&amp;quot;results-row' + position + '&amp;quot;&amp;gt;',
												'&amp;lt;td&amp;gt;',
													'&amp;lt;h3&amp;gt;',
														title,
													'&amp;lt;/h3&amp;gt;',
													'&amp;lt;p&amp;gt;',
														'&amp;lt;img style=&amp;quot;float: left;&amp;quot; src=&amp;quot;',
															image,
															'&amp;quot; alt=&amp;quot;',
															name,
															'&amp;quot;/&amp;gt;',
														//description,
													'&amp;lt;/p&amp;gt;',
													'&amp;lt;a href=&amp;quot;',
														document,
														'&amp;quot;&amp;gt;Spec Sheet&amp;lt;/a&amp;gt;',
												'&amp;lt;/td&amp;gt;',
											'&amp;lt;/tr&amp;gt;'
										].join('')
									);
								}
							}
						}
					}
				);
			}

			search();
		}
	);		
	&amp;lt;/script&amp;gt;
#end&lt;/pre&gt; &lt;p&gt;You'll notice that the bulk of the code lies in javascript processing the results into the table. I'm not the greatest of javascript developers so take my code with a grain of salt.&lt;/p&gt; &lt;p&gt;Also notice that the target of the AJAX call is a url generated from the request and is in fact one which invokes the current portlet in the &amp;quot;RESOURCE_PHASE&amp;quot;. If you aren't familiar with portlets, the &amp;quot;RESOURCE_PHASE&amp;quot; is one which allows portlets to return output without the wrappings and trappings of the surrounding portal. Effectively the OutputStream or PrintWriter used by the portlet is not touched or altered in any way by the portal. This allows the portlet to do things like handle AJAX requests, or generally server any type of &amp;quot;static&amp;quot; resource, like images.&lt;/p&gt; &lt;p&gt;Finally, see how the value of each individual structure field is retrieved from the JSON object: &lt;span class="tt"&gt;doc.fields.map['web_content/&lt;span class="b"&gt;name&lt;/span&gt;'].value&lt;/span&gt;. Each field that is marked as &amp;quot;Searchable&amp;quot; when creating the structure can be retrieved from the SearchEngine result prefixed by &lt;span class="tt"&gt;web_content/&lt;/span&gt;. The prefix exists so that dynamically created fields don't collide with fields of the actual Web Content Article object when indexed.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;The final step is getting our content! To do that we'll invoke and query Liferay's SearchEngine from within the template and convert the results into JSON format which we will return as the response body of the request.&lt;/p&gt; &lt;pre&gt;
...
#if ($request.lifecycle == 'RESOURCE_PHASE')
	#set ($portalBeanLocator = $portal.getClass().forName('com.liferay.portal.kernel.bean.PortalBeanLocatorUtil'))

	#set ($searchEngine = $portalBeanLocator.locate('com.liferay.portal.kernel.search.SearchEngineUtil'))
	#set ($queryFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.search.BooleanQueryFactoryUtil'))
	#set ($sortFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.search.SortFactoryUtil'))
	#set ($jsonFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.json.JSONFactoryUtil'))

	#set ($fullQuery = $queryFactory.create())
	#set ($contextQuery = $queryFactory.create())
	
	#set ($V = $contextQuery.addRequiredTerm('companyId', $companyId))
	#set ($V = $contextQuery.addExactTerm('entryClassName', 'com.liferay.portlet.journal.model.JournalArticle'))
	#set ($V = $contextQuery.addRequiredTerm('groupId', $scopeGroupId))
	&lt;span class="b"&gt;#set ($V = $contextQuery.addRequiredTerm('structureId', '10628'))&lt;/span&gt;
	#set ($V = $fullQuery.add($contextQuery, 'MUST'))
	
	#set ($sorts = $sortFactory.getDefaultSorts())
	
	#set ($hits = $searchEngine.search($companyId, $fullQuery, $sorts, 0, ))

	$jsonFactory.serialize($hits)
#else
...&lt;/pre&gt; &lt;p&gt;What I've done here is create a SearchEngine query which limits the results to a specific structureId. This limitation is only imposed because I chose to limit the logic of the example to only support this one structure. It is entirely possible to query for arbitrary content types (even beyond just Web Content) as long as you are willing to implement the view logic to handle those.&lt;/p&gt; &lt;p&gt;Here's an image of what it might look like.&lt;br /&gt; &lt;img width="500" height="463" alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=ed37f8a5-b5c2-4f28-8603-0beb8dbf3811&amp;amp;groupId=10529&amp;amp;t=1296679489180" /&gt;&lt;/p&gt; &lt;p&gt;The complete template follows:&lt;/p&gt; &lt;pre&gt;
#set ($ns = $request.portlet-namespace)
#set ($companyId = $getterUtil.getLong($request.theme-display.company-id))
#set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id))
#set ($numberOfItems = $getterUtil.getInteger($number-of-items.data))

#if ($request.lifecycle == 'RESOURCE_PHASE')
	#set ($portalBeanLocator = $portal.getClass().forName('com.liferay.portal.kernel.bean.PortalBeanLocatorUtil'))

	#set ($searchEngine = $portalBeanLocator.locate('com.liferay.portal.kernel.search.SearchEngineUtil'))
	#set ($queryFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.search.BooleanQueryFactoryUtil'))
	#set ($sortFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.search.SortFactoryUtil'))
	#set ($jsonFactory = $portalBeanLocator.locate('com.liferay.portal.kernel.json.JSONFactoryUtil'))

	#set ($fullQuery = $queryFactory.create())
	#set ($contextQuery = $queryFactory.create())
	
	#set ($V = $contextQuery.addRequiredTerm('companyId', $companyId))
	#set ($V = $contextQuery.addExactTerm('entryClassName', 'com.liferay.portlet.journal.model.JournalArticle'))
	#set ($V = $contextQuery.addRequiredTerm('groupId', $scopeGroupId))
	#set ($V = $contextQuery.addRequiredTerm('structureId', '10628'))
	#set ($V = $fullQuery.add($contextQuery, 'MUST'))
	
	#set ($sorts = $sortFactory.getDefaultSorts())
	
	#set ($hits = $searchEngine.search($companyId, $fullQuery, $sorts, 0, $numberOfItems))

	$jsonFactory.serialize($hits)
#else
	&amp;lt;table class=&amp;quot;taglib-search-iterator&amp;quot;&amp;gt;
		&amp;lt;thead&amp;gt;
			&amp;lt;tr class=&amp;quot;portlet-section-header results-header&amp;quot;&amp;gt;
				&amp;lt;th&amp;gt;
					Widgets
				&amp;lt;/th&amp;gt;
			&amp;lt;/tr&amp;gt;
		&amp;lt;/thead&amp;gt;
		&amp;lt;tbody class=&amp;quot;${ns}results-container&amp;quot;&amp;gt;
			&amp;lt;tr class=&amp;quot;portlet-section-body results-row last&amp;quot;&amp;gt;
				&amp;lt;td&amp;gt;
					No Widgets
				&amp;lt;/td&amp;gt;
			&amp;lt;/tr&amp;gt;
		&amp;lt;/tbody&amp;gt;
	&amp;lt;/table&amp;gt;

	&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
	AUI().use(
		'aui-base', 'aui-io',
		function(A) {
			var search = function(eventType) {
				A.io.request(
					'${request.resource-url}',
					{
						dataType: 'json',
						on: {
							success: function(event, id, obj) {
								var instance = this;

								var hits = instance.get('responseData');
								
								var resultsContainer = A.one('.${ns}results-container');
						
								if (!hits &amp;amp;&amp;amp; !hits.docs) {
									return;
								}

								resultsContainer.empty();
								
								for (var i = 0; i &amp;lt; hits.docs.length; i++) {
									var doc = hits.docs[i];

									console.log(doc);
								
									var title = doc.fields.map['web_content/name'].value || doc.fields.map.uid.value;
									var description = doc.fields.map['web_content/description'].value;
									var image = doc.fields.map['web_content/image'].value;
									var document = doc.fields.map['web_content/document'].value;
									
									var position = ' portlet-section-body';
									
									if (i % 2 == 1) {
										position = ' portlet-section-alternate alt';
									}
									
									if (i == 0) {
										position += ' first';
									}
									else if (i == hits.length - 1) {
										position += ' last';
									}
		
									resultsContainer.append(
										[
											'&amp;lt;tr class=&amp;quot;results-row' + position + '&amp;quot;&amp;gt;',
												'&amp;lt;td&amp;gt;',
													'&amp;lt;h3&amp;gt;',
														title,
													'&amp;lt;/h3&amp;gt;',
													'&amp;lt;p&amp;gt;',
														'&amp;lt;img style=&amp;quot;float: left;&amp;quot; src=&amp;quot;',
															image,
															'&amp;quot; alt=&amp;quot;',
															name,
															'&amp;quot;/&amp;gt;',
														//description,
													'&amp;lt;/p&amp;gt;',
													'&amp;lt;a href=&amp;quot;',
														document,
														'&amp;quot;&amp;gt;Spec Sheet&amp;lt;/a&amp;gt;',
												'&amp;lt;/td&amp;gt;',
											'&amp;lt;/tr&amp;gt;'
										].join('')
									);
								}
							}
						}
					}
				);
			}

			search();
		}
	);		
	&amp;lt;/script&amp;gt;
#end&lt;/pre&gt;  &lt;p&gt;Here it is again as XSLT!&lt;/p&gt;  &lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;

&amp;lt;xsl:stylesheet version=&amp;quot;1.0&amp;quot;
	xmlns:BooleanQueryFactoryUtil=&amp;quot;xalan://com.liferay.portal.kernel.search.BooleanQueryFactoryUtil&amp;quot;
	xmlns:JSONFactoryUtil=&amp;quot;xalan://com.liferay.portal.kernel.json.JSONFactoryUtil&amp;quot;
	xmlns:BooleanQuery=&amp;quot;xalan://com.liferay.portal.kernel.search.BooleanQuery&amp;quot;
	xmlns:SearchEngineUtil=&amp;quot;xalan://com.liferay.portal.kernel.search.SearchEngineUtil&amp;quot;
	xmlns:SortFactoryUtil=&amp;quot;xalan://com.liferay.portal.kernel.search.SortFactoryUtil&amp;quot;
	xmlns:str=&amp;quot;http://exslt.org/strings&amp;quot;
	xmlns:xalan=&amp;quot;http://xml.apache.org/xalan&amp;quot;
	xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;
	exclude-result-prefixes=&amp;quot;xalan&amp;quot;
	extension-element-prefixes=&amp;quot;BooleanQueryFactoryUtil JSONFactoryUtil BooleanQuery SearchEngineUtil SortFactoryUtil str xalan&amp;quot;&amp;gt;

	&amp;lt;xsl:output method=&amp;quot;text&amp;quot; omit-xml-declaration=&amp;quot;yes&amp;quot; /&amp;gt;
	
	&amp;lt;xsl:param name=&amp;quot;groupId&amp;quot; /&amp;gt;

	&amp;lt;xsl:variable name=&amp;quot;request&amp;quot; select=&amp;quot;/root/request&amp;quot; /&amp;gt;
	&amp;lt;xsl:variable name=&amp;quot;ns&amp;quot; select=&amp;quot;$request/portlet-namespace&amp;quot; /&amp;gt;
	&amp;lt;xsl:variable name=&amp;quot;companyId&amp;quot; select=&amp;quot;$request/theme-display/company-id&amp;quot; /&amp;gt;
	&amp;lt;xsl:variable name=&amp;quot;scopeGroupId&amp;quot; select=&amp;quot;$request/theme-display/scope-group-id&amp;quot; /&amp;gt;
	&amp;lt;xsl:variable name=&amp;quot;numberOfItems&amp;quot; select=&amp;quot;/root/dynamic-element[@name='number-of-items']/dynamic-content&amp;quot; /&amp;gt;

	&amp;lt;xsl:template name=&amp;quot;out&amp;quot; match=&amp;quot;@*|node()&amp;quot;&amp;gt;
		&amp;lt;xsl:value-of select=&amp;quot;local-name()&amp;quot;/&amp;gt;&amp;lt;xsl:text&amp;gt; = &amp;lt;/xsl:text&amp;gt;
		&amp;lt;xsl:copy&amp;gt;
			&amp;lt;xsl:apply-templates select=&amp;quot;@*|node()&amp;quot;/&amp;gt;
		&amp;lt;/xsl:copy&amp;gt;
	&amp;lt;/xsl:template&amp;gt;

	&amp;lt;xsl:template match=&amp;quot;/&amp;quot;&amp;gt;
		&amp;lt;xsl:choose&amp;gt;
			&amp;lt;xsl:when test=&amp;quot;$request/lifecycle = 'RESOURCE_PHASE'&amp;quot;&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;fullQuery&amp;quot; select=&amp;quot;BooleanQueryFactoryUtil:create()&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;contextQuery&amp;quot; select=&amp;quot;BooleanQueryFactoryUtil:create()&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;void1&amp;quot; select=&amp;quot;BooleanQuery:addRequiredTerm($contextQuery, 'companyId', $companyId)&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;void2&amp;quot; select=&amp;quot;BooleanQuery:addExactTerm($contextQuery, 'entryClassName', 'com.liferay.portlet.journal.model.JournalArticle')&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;void3&amp;quot; select=&amp;quot;BooleanQuery:addRequiredTerm($contextQuery, 'groupId', $scopeGroupId)&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;void4&amp;quot; select=&amp;quot;BooleanQuery:addRequiredTerm($contextQuery, 'structureId', '10628')&amp;quot; /&amp;gt;
				&amp;lt;xsl:variable name=&amp;quot;void5&amp;quot; select=&amp;quot;BooleanQuery:add($fullQuery, $contextQuery, 'MUST')&amp;quot; /&amp;gt;

				&amp;lt;xsl:variable name=&amp;quot;sorts&amp;quot; select=&amp;quot;SortFactoryUtil:getDefaultSorts()&amp;quot; /&amp;gt;
				
				&amp;lt;xsl:message&amp;gt;
					&amp;lt;xsl:value-of select=&amp;quot;$numberOfItems&amp;quot; /&amp;gt;
				&amp;lt;/xsl:message&amp;gt;
				
				&amp;lt;xsl:variable name=&amp;quot;hits&amp;quot; select=&amp;quot;SearchEngineUtil:search(number($companyId), $fullQuery, $sorts, number(0), number($numberOfItems))&amp;quot; /&amp;gt;
				
				&amp;lt;xsl:value-of select=&amp;quot;JSONFactoryUtil:serialize($hits)&amp;quot; /&amp;gt;
			&amp;lt;/xsl:when&amp;gt;
			&amp;lt;xsl:otherwise&amp;gt;
				&amp;lt;xsl:text disable-output-escaping=&amp;quot;yes&amp;quot;&amp;gt;&amp;lt;![CDATA[
					&amp;lt;table class=&amp;quot;taglib-search-iterator&amp;quot;&amp;gt;
						&amp;lt;thead&amp;gt;
							&amp;lt;tr class=&amp;quot;portlet-section-header results-header&amp;quot;&amp;gt;
								&amp;lt;th&amp;gt;
									Widgets
								&amp;lt;/th&amp;gt;
							&amp;lt;/tr&amp;gt;
						&amp;lt;/thead&amp;gt;
						&amp;lt;tbody class=&amp;quot;]]&amp;gt;&amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;$ns&amp;quot; /&amp;gt;&amp;lt;xsl:text disable-output-escaping=&amp;quot;yes&amp;quot;&amp;gt;&amp;lt;![CDATA[results-container&amp;quot;&amp;gt;
							&amp;lt;tr class=&amp;quot;portlet-section-body results-row last&amp;quot;&amp;gt;
								&amp;lt;td&amp;gt;
									No Widgets
								&amp;lt;/td&amp;gt;
							&amp;lt;/tr&amp;gt;
						&amp;lt;/tbody&amp;gt;
					&amp;lt;/table&amp;gt;]]&amp;gt;&amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:text disable-output-escaping=&amp;quot;yes&amp;quot;&amp;gt;&amp;lt;![CDATA[
					&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
					AUI().use(
						'aui-base', 'aui-io',
						function(A) {
							var search = function(eventType) {
								A.io.request(
									']]&amp;gt;&amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of disable-output-escaping=&amp;quot;yes&amp;quot; select=&amp;quot;$request/resource-url&amp;quot; /&amp;gt;&amp;lt;xsl:text disable-output-escaping=&amp;quot;yes&amp;quot;&amp;gt;&amp;lt;![CDATA[',
									{
										dataType: 'json',
										on: {
											success: function(event, id, obj) {
												var instance = this;

												var hits = instance.get('responseData');
								
												var resultsContainer = A.one('.]]&amp;gt;&amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;$ns&amp;quot; /&amp;gt;&amp;lt;xsl:text disable-output-escaping=&amp;quot;yes&amp;quot;&amp;gt;&amp;lt;![CDATA[results-container');
						
												resultsContainer.empty();
								
												if (!hits &amp;amp;&amp;amp; !hits.docs) {
													return;
												}

												for (var i = 0; i &amp;lt; hits.docs.length; i++) {
													var doc = hits.docs[i];

													var title = doc.fields.map['web_content/name'].value || doc.fields.map.uid.value;
													var description = doc.fields.map['web_content/description'].value;
													var image = doc.fields.map['web_content/image'].value;
													var document = doc.fields.map['web_content/document'].value;
									
													var position = ' portlet-section-body';
									
													if (i % 2 == 1) {
														position = ' portlet-section-alternate alt';
													}
									
													if (i == 0) {
														position += ' first';
													}
													else if (i == hits.length - 1) {
														position += ' last';
													}
		
													resultsContainer.append(
														[
															'&amp;lt;tr class=&amp;quot;results-row' + position + '&amp;quot;&amp;gt;',
																'&amp;lt;td&amp;gt;',
																	'&amp;lt;h3&amp;gt;',
																		title,
																	'&amp;lt;/h3&amp;gt;',
																	'&amp;lt;p&amp;gt;',
																		'&amp;lt;img style=&amp;quot;float: left;&amp;quot; src=&amp;quot;',
																			image,
																			'&amp;quot; alt=&amp;quot;',
																			name,
																			'&amp;quot;/&amp;gt;',
																		//description,
																	'&amp;lt;/p&amp;gt;',
																	'&amp;lt;a href=&amp;quot;',
																		document,
																		'&amp;quot;&amp;gt;Spec Sheet&amp;lt;/a&amp;gt;',
																'&amp;lt;/td&amp;gt;',
															'&amp;lt;/tr&amp;gt;'
														].join('')
													);
												}
											}
										}
									}
								);
							}

							search();
						}
					);		
					&amp;lt;/script&amp;gt;]]&amp;gt;&amp;lt;/xsl:text&amp;gt;
			&amp;lt;/xsl:otherwise&amp;gt;
		&amp;lt;/xsl:choose&amp;gt;
	&amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;&lt;/pre&gt;  &lt;p&gt;Here's hoping someone finds this useful!&lt;/p&gt; &lt;style type="text/css"&gt;
.post-body {
}
p.quote {
	margin: 1em 2em;
}
pre {
	background-color: #DDD;
	border: 1px solid #999;
	font: normal 10px sans-serif;
	max-height: 300px;
	padding: 5px;
	overflow: auto;
	margin-bottom: 12px;
}
span.attr {
	display: block;
	text-align: right;
	font-size: smaller;
}
span.new {
	background-color: #bbccff;
}
span.tt {
	font-family: Courier, monospace;
}
span.b {
	font-weight: bold;
}
span.em {
	font-style: italic;
}
span.little {
	font-size: smaller;
}
&lt;/style&gt;&lt;/div&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-02-02T19:05:16Z</dc:date>
  </entry>
  <entry>
    <title>Liferay Expando MongoDB Hook Finished Review</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-expando-mongodb-hook-finished-review" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-expando-mongodb-hook-finished-review</id>
    <updated>2011-01-17T19:47:51Z</updated>
    <published>2011-01-17T19:39:31Z</published>
    <summary type="html">&lt;p&gt;Last week I wrote about a hook we wrote for &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-iii-liferay-nosql-and-mongodb-"&gt;Expandos using MongoDB&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I just wanted to let you know that it just finished review and is ready for consumption.&lt;/p&gt;&lt;p&gt;It's been renamed to &lt;a href="http://svn.liferay.com/repos/public/plugins/trunk/hooks/mongodb-hook"&gt;mongodb-hook&lt;/a&gt; in case we ever want to persist more services to it.&lt;/p&gt;&lt;p&gt;Have a look and enjoy!&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-01-17T19:39:31Z</dc:date>
  </entry>
  <entry>
    <title>Expandos III (Liferay, NoSQL, and MongoDB)</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-iii-liferay-nosql-and-mongodb-" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-iii-liferay-nosql-and-mongodb-</id>
    <updated>2011-02-04T23:33:28Z</updated>
    <published>2011-01-09T04:06:49Z</published>
    <summary type="html">&lt;div class="post-body"&gt;&lt;p&gt;&lt;span class="b"&gt;Update:&lt;/span&gt; The expando-mongodb-hook plugin is now committed to SVN and available from trunk.&lt;/p&gt; &lt;p&gt;Over the past few months the hype around &lt;span class="em"&gt;NoSQL&lt;/span&gt; type databases had really  been heating up the tech news and blog feeds. There seems to be an overwhelming desire to find scallability  solutions that don't seem to be addressed with an RDBMS. What is Liferay to do?&lt;/p&gt; &lt;p&gt;Could Liferay support some  form of NoSQL integration? I think so, and I surely couldn't go long without doing something to draw attention to the fact that Liferay is a prime candidate as a viable platform for scalling dynamically via a NoSQL backend.&lt;/p&gt; &lt;p&gt;The most obvious way I could see to leverage a NoSQL solution was with perhaps the most dynamic aspect of the portal, &lt;span class="b"&gt;Expandos&lt;/span&gt; (and by association &lt;span class="b"&gt;Custom  Fields&lt;/span&gt;).&lt;/p&gt; &lt;p&gt;In order prove the concept of NoSQL with Liferay we decided to write an adapter (using a &lt;span class="b"&gt;Liferay  Hook&lt;/span&gt; pattern) to build a backend for Expando on &lt;span class="b"&gt;MongoDB&lt;/span&gt;. I had no real idea how long it would take to accomplish but we decided to try. As it turns out it was not too difficult. We now have a fully functional adapter to store all of Liferay's dynamic Expando data in a highly scalable MongoDB. But note that Expandos still support all Liferay permissions. And Custom Fields are still indexed along with the entities anywhere they would be normally.  This is a fantastic demonstration of just how extensible Liferay portal really is.&lt;/p&gt; &lt;p&gt;I tested against the version of mongodb that was readily available for &lt;span class="tt b"&gt;Ubuntu 10.04 (1:1.2.2-1ubuntu1.1)&lt;/span&gt;.  I also tried to make sure to support cluster configurations. So check out the portlet.properties file in the plugin as well as  the mongodb driver javadocs for what and how to set that up.&lt;/p&gt; &lt;p&gt;I did several small usage tests (none of which were load testing, since this was an informal design) to see  that everything was working the right way. I created several Custom Fields on several different entites and  tested CRUD opperations to make sure that the data was landing (as well as being removed/updated) where I  wanted it, in MongoDB.&lt;/p&gt; &lt;p&gt;Meanwhile, I was also using the Mongo DB command line client &lt;span class="tt b"&gt;mongo&lt;/span&gt; to make sure  that everything was working from that end. I added a custom field called &lt;span class="tt"&gt;test&lt;/span&gt; to the  &lt;span class="tt"&gt;User&lt;/span&gt; entity, and for the first user in the system, I set the value to  &lt;span class="tt"&gt;test value&lt;/span&gt; . Here is an example of what we see via  &lt;span class="tt"&gt;mongo&lt;/span&gt;:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;pre&gt;
[rotty@rotty-desktop  expando-mongodb-hook]$ mongo
MongoDB shell version: 1.2.2
url: test
connecting to: test
type &amp;quot;exit&amp;quot; to exit
type &amp;quot;help&amp;quot; for help
&lt;span class="b"&gt;&amp;gt; show dbs&lt;/span&gt;
admin
local
lportal_0
lportal_10135
&lt;span class="b"&gt;&amp;gt; use lportal_10135&lt;/span&gt;
switched to db lportal_10135
&lt;span class="b"&gt;&amp;gt; db.getCollectionNames()&lt;/span&gt;
[
	&amp;quot;com.liferay.portal.model.User#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;com.liferay.portlet.blogs.model.BlogsEntry#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;com.liferay.portlet.documentlibrary.model.DLFileEntry#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;system.indexes&amp;quot;
]
&lt;span class="b"&gt;&amp;gt; db.getCollection(&amp;quot;com.liferay.portal.model.User#CUSTOM_FIELDS&amp;quot;).count()&lt;/span&gt;
1
&lt;span class="b"&gt;&amp;gt; db.getCollection(&amp;quot;com.liferay.portal.model.User#CUSTOM_FIELDS&amp;quot;).find()&lt;/span&gt;
{ &amp;quot;_id&amp;quot; : ObjectId(&amp;quot;4d28f318fcfcc08a7855ebe4&amp;quot;), &amp;quot;companyId&amp;quot; : 10135, &amp;quot;tableId&amp;quot; : 17205, &amp;quot;rowId&amp;quot; : 10173, &amp;quot;classNameId&amp;quot; : 10048, &amp;quot;classPK&amp;quot; : 10173, &amp;quot;valueId&amp;quot; : 17207, &lt;span class="b"&gt;&amp;quot;test&amp;quot; : &amp;quot;test value&amp;quot;&lt;/span&gt; }
&amp;gt; 
&lt;/pre&gt; &lt;p&gt;So far so good! As you can see the data is landing nicely into the Mongo DB database.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;div class="separator"&gt;&lt;!----&gt;&lt;/div&gt; &lt;p&gt;While that was a good test I also wanted to make sure that other use cases would work just as well. I  decided to revive the &lt;span class="b"&gt;&lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-ii-refactor-of-a-previous-post-for-6-0"&gt;First Expando Bank&lt;/a&gt;&lt;/span&gt;  example to see how that would work.&lt;/p&gt; &lt;p&gt;I first had to make a few small API changes in the Velocity template. The updated template is attached. See  &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-what-are-they-and-how-do-they-help-me-liferay-portal-5-0-1"&gt;this article&lt;/a&gt; and the  &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/expandos-ii-refactor-of-a-previous-post-for-6-0"&gt;follow up&lt;/a&gt; for more information on that topic.&lt;/p&gt; &lt;p&gt;After adding some accounts into the &lt;span class="b"&gt;First Expando Bank&lt;/span&gt; app, the &lt;span class="tt"&gt;mongo&lt;/span&gt;  console results looked like this:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;pre&gt;&lt;span class="b"&gt;&amp;gt; db.getCollectionNames()&lt;/span&gt;
[
	&lt;span class="b"&gt;&amp;quot;AccountsTable#AccountsTable&amp;quot;&lt;/span&gt;,
	&amp;quot;com.liferay.portal.model.User#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;com.liferay.portlet.blogs.model.BlogsEntry#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;com.liferay.portlet.documentlibrary.model.DLFileEntry#CUSTOM_FIELDS&amp;quot;,
	&amp;quot;system.indexes&amp;quot;
]
&lt;span class="b"&gt;&amp;gt; db.getCollection(&amp;quot;AccountsTable#AccountsTable&amp;quot;).count()&lt;/span&gt;
3
&lt;span class="b"&gt;&amp;gt; db.getCollection(&amp;quot;AccountsTable#AccountsTable&amp;quot;).find()&lt;/span&gt;
{ &amp;quot;_id&amp;quot; : ObjectId(&amp;quot;4d29292abda2c08a05e35e67&amp;quot;), &amp;quot;companyId&amp;quot; : 10135, &amp;quot;tableId&amp;quot; : 17320, &amp;quot;rowId&amp;quot; : 1294543146642, &amp;quot;classNameId&amp;quot; : 17313, &amp;quot;classPK&amp;quot; : 1294543146642, &amp;quot;valueId&amp;quot; : 17336, &amp;quot;balance&amp;quot; : 55, &amp;quot;firstName&amp;quot; : &amp;quot;Ray&amp;quot;, &amp;quot;lastName&amp;quot; : &amp;quot;Auge&amp;quot;, &amp;quot;modifiedDate&amp;quot; : &amp;quot;Sat Jan 08 2011 22:19:06 GMT-0500 (EST)&amp;quot; }
{ &amp;quot;_id&amp;quot; : ObjectId(&amp;quot;4d292945bda2c08a06e35e67&amp;quot;), &amp;quot;companyId&amp;quot; : 10135, &amp;quot;tableId&amp;quot; : 17320, &amp;quot;rowId&amp;quot; : 1294543173086, &amp;quot;classNameId&amp;quot; : 17313, &amp;quot;classPK&amp;quot; : 1294543173086, &amp;quot;valueId&amp;quot; : 17337, &amp;quot;balance&amp;quot; : 120, &amp;quot;firstName&amp;quot; : &amp;quot;Daffy&amp;quot;, &amp;quot;lastName&amp;quot; : &amp;quot;Duck&amp;quot;, &amp;quot;modifiedDate&amp;quot; : &amp;quot;Sat Jan 08 2011 22:19:33 GMT-0500 (EST)&amp;quot; }
{ &amp;quot;_id&amp;quot; : ObjectId(&amp;quot;4d292958bda2c08a07e35e67&amp;quot;), &amp;quot;companyId&amp;quot; : 10135, &amp;quot;tableId&amp;quot; : 17320, &amp;quot;rowId&amp;quot; : 1294543192848, &amp;quot;classNameId&amp;quot; : 17313, &amp;quot;classPK&amp;quot; : 1294543192848, &amp;quot;valueId&amp;quot; : 17338, &amp;quot;balance&amp;quot; : 300, &amp;quot;firstName&amp;quot; : &amp;quot;Mickey&amp;quot;, &amp;quot;lastName&amp;quot; : &amp;quot;Mouse&amp;quot;, &amp;quot;modifiedDate&amp;quot; : &amp;quot;Sat Jan 08 2011 22:19:52 GMT-0500 (EST)&amp;quot; }
&lt;span class="b"&gt;&amp;gt; &lt;/span&gt;&lt;/pre&gt; &lt;p&gt;Excelent! It would appear that all our use cases are covered from automatic Custom Fields  via the UI to programmatic use in a CMS template.&lt;/p&gt; &lt;p&gt;I'd love to get your feedback about it! Please note that there is currently no rich way to perform queries (à la MongoDB). But with a little enginuity we could probably make that possible.&lt;/p&gt; &lt;style type="text/css"&gt;
.post-body {
	width: 98%;
}
p.quote {
	margin: 1em 2em;
}
pre {
	background-color: #DDD;
	border: 1px solid #999;
	font: normal 10px sans-serif;
	max-height: 200px;
	padding: 5px;
	overflow: auto;
	margin-bottom: 12px;
}
span.attr {
	display: block;
	text-align: right;
	font-size: smaller;
}
span.new {
	background-color: #bbccff;
}
span.tt {
	font-family: Courier, monospace;
}
span.b {
	font-weight: bold;
}
span.em {
	font-style: italic;
}
span.little {
	font-size: smaller;
}
&lt;/style&gt;&lt;/div&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2011-01-09T04:06:49Z</dc:date>
  </entry>
  <entry>
    <title>Liferay Nomination to the JCP</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-nomination-to-the-jcp" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-nomination-to-the-jcp</id>
    <updated>2010-10-26T14:14:58Z</updated>
    <published>2010-10-25T15:09:55Z</published>
    <summary type="html">&lt;p&gt;The JCP Election is just around the corner and Liferay is on the ballot for the first time ever. Being that it is the first time, you may want to know what motivates Liferay's desire for a seat on the JCP?&lt;br /&gt;&lt;br /&gt;Frankly it's not a political one. It's purely pragmatic! Liferay would like to see things get done, to see the JCP process in the most effective state it can be. Liferay is not new to the java world and Liferay knows open source. It knows it well as a provider as well as a consumer being itself one of the largest consumers of open source java projects in the industry. On the other hand Liferay is aware of the need to drive revenue whether as an individual or as an enterprise. So, we like to not waste time and get things done. There is no political engine at work. There is only reality which demands that solutions arrive in a timely fashion; and yet Liferay is keenly aware that standardization is nessecary and can take time. Adressing the needs of a wide audience can be difficult and filled with roadblocks beyond just the technical ones. To that end Liferay wants to bring it's pragmatic know how to bare and drive innovation at it's heart. There is no better venue than the JCP.&lt;br /&gt;&lt;br /&gt;So then, the key techincal concerns for Liferay are primarly to preserve an open, unfragmented, ubiquitous Java platform, to see improved development agility, to strive for increased modularity and to reach those goals sooner than later. The future looks bright if the JCP's focus can stay on track and Liferay would like to help make that happen.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-10-25T15:09:55Z</dc:date>
  </entry>
  <entry>
    <title>Custom Velocity Tools and Liferay 6.0</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/custom-velocity-tools-and-liferay-6-0" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/custom-velocity-tools-and-liferay-6-0</id>
    <updated>2011-02-04T23:33:47Z</updated>
    <published>2010-10-04T13:52:44Z</published>
    <summary type="html">&lt;div&gt;&lt;p&gt;A while back I wrote a &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/custom-velocity-tools"&gt;post about adding custom tools to the Liferay Velocity context&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;In 6.0 a change was made such that the behaviour has changed slightly. Now all such tools are plain old beans which must implement an interface.&lt;/p&gt; &lt;p&gt;The changes also means that I have a lot less code to write and less wiring to do. Let's see how we'd do it now using exactly the same tool as that old post.&lt;/p&gt; &lt;p&gt;The interface again:&lt;/p&gt; &lt;pre&gt;
package com.mytool;

public interface MyTool {

	public String operationOne();

	public String operationTwo(String name);

}&lt;/pre&gt; &lt;p&gt;We need an implementation of the interface:&lt;/p&gt; &lt;pre&gt;
package com.mytool;

public class MyToolImpl implements MyTool {

	public String operationOne() {
		return &amp;quot;Hello out there!&amp;quot;;
	}

	public String operationTwo(String name) {
		return &amp;quot;Hello &amp;quot; + name + &amp;quot;!&amp;quot;;
	}

}&lt;/pre&gt; &lt;p&gt;Our spring configuration only requires a single bean definition:&lt;/p&gt; &lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE beans PUBLIC &amp;quot;-//SPRING//DTD BEAN//EN&amp;quot; &amp;quot;http://www.springframework.org/dtd/spring-beans.dtd&amp;quot;&amp;gt;

&amp;lt;beans&amp;gt;
	&amp;lt;bean id=&amp;quot;com.mytool.MyTool&amp;quot; class=&amp;quot;com.mytool.MyToolImpl&amp;quot; /&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/pre&gt; &lt;p&gt;Of course in order for our bean definitions to be read we need to make sure it gets loaded, so we'll name it so that it's found by the context loader &lt;span class="tt"&gt;WEB-INF/applicationContext.xml&lt;/span&gt; (We could have used the portal-esk spring config mechanism, but I wanted to demonstrate that Liferay is flexible and sensitive to existing coding behaviors.&lt;/p&gt; &lt;p&gt;I mentioned that we need a context loader, so there is one more change required. While before you could only add tools within a ServiceBuilder enabled plugin, that is no longer required. All you have to do is add the following context loader listener the your &lt;span class="tt"&gt;web.xml&lt;/span&gt; (but only if your plugin is not a portlet type plugin):&lt;/p&gt; &lt;pre&gt;
    &amp;lt;listener&amp;gt;
        &amp;lt;listener-class&amp;gt;com.liferay.portal.kernel.spring.context.PortletContextLoaderListener&amp;lt;/listener-class&amp;gt;
    &amp;lt;/listener&amp;gt;
&lt;/pre&gt; &lt;p&gt;This effectively tells the portal to create a BeanLocator object associated with your plugin and to read the beans definitions that were defined in &lt;span class="tt"&gt;WEB-INF/applicationContext.xml&lt;/span&gt;.&lt;/p&gt; &lt;p&gt;Now we're ready to use our tool in a Velocity template:&lt;/p&gt; &lt;p&gt;Since we've done this in a plugin, you will have to specify the 'contextPathName' of the plugin so that the appropriate BeanLocator can be used to lookup your tool. For example, the context path name of your plugin being &amp;quot;test-velotool-hook&amp;quot;, then you'd use the following in your template:&lt;/p&gt; &lt;pre&gt;
#set ($myTool = $utilLocator.findUtil('test-velotool-hook', 'com.mytool.MyTool'))

$myTool.operationOne()

$myTool.operationTwo('Ray')&lt;/pre&gt; &lt;p&gt;I've linked a source hook plugin that you can drop into your plugins SDK and deploy just by doing &lt;span class="tt"&gt;ant deploy&lt;/span&gt;.&lt;/p&gt; &lt;p&gt;[&lt;a href="http://www.liferay.com/c/document_library/get_file?uuid=b92ee8b1-63f6-49e9-a9d3-04a56ec17267&amp;amp;groupId=10529"&gt;test-velotool-hook.zip&lt;/a&gt;]&lt;/p&gt; &lt;style type="text/css"&gt;
.post-body {
}
p.quote {
	margin: 1em 2em;
}
pre {
	background-color: #DDD;
	border: 1px solid #999;
	font: normal 10px sans-serif;
	max-height: 300px;
	padding: 5px;
	overflow: auto;
	margin-bottom: 12px;
}
span.attr {
	display: block;
	text-align: right;
	font-size: smaller;
}
span.new {
	background-color: #bbccff;
}
span.tt {
	font-family: Courier, monospace;
}
span.b {
	font-weight: bold;
}
span.em {
	font-style: italic;
}
span.little {
	font-size: smaller;
}
&lt;/style&gt;&lt;/div&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-10-04T13:52:44Z</dc:date>
  </entry>
  <entry>
    <title>Debugging Liferay in Eclipse</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/debugging-liferay-in-eclipse" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/debugging-liferay-in-eclipse</id>
    <updated>2011-10-24T14:12:59Z</updated>
    <published>2010-09-22T14:23:47Z</published>
    <summary type="html">&lt;p&gt;
	When should you use a debugger during development? At ALL times! At least that's my view.&lt;/p&gt;
&lt;p&gt;
	Isn't that a hassle? Well debugging sure can be, but if you setup your environment in a specific way, it can actually become very fast and almost transaprent. I like transparent!&lt;/p&gt;
&lt;p&gt;
	Here are steps I take to configure my tomcat (I've followed the same steps with JBoss):&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;
		Note the deployment path of tomcat (in my case I'll use &lt;code&gt;/bundles/tomcat-6.0.x&lt;/code&gt;)&lt;/li&gt;
	&lt;li&gt;
		In Eclipse, open the "Run Configurations" manager (Run -&amp;gt; Run Configurations...)&lt;/li&gt;
	&lt;li&gt;
		On the "Java Application" node, right click and choose "New".&lt;/li&gt;
	&lt;li&gt;
		On the first tab (Main) select the project you are running in tomcat&lt;/li&gt;
	&lt;li&gt;
		In the Main Class field enter &lt;code&gt;org.apache.catalina.startup.Bootstrap&lt;br /&gt;
		&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;
		On the Arguments tab, in the Program Arguments field enter &lt;code&gt;start&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;
		In the VM&amp;nbsp;arguments field enter the following:&lt;br /&gt;
		&lt;br /&gt;
		&lt;code&gt;-Xms1024m&lt;br /&gt;
		-Xmx1024m&lt;br /&gt;
		-XX:PermSize=128m&lt;br /&gt;
		-XX:MaxPermSize=256m&lt;br /&gt;
		-XX:+CMSClassUnloadingEnabled&lt;br /&gt;
		-Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=true&lt;br /&gt;
		-Dcatalina.base=/bundles/tomcat-6.0.x&lt;br /&gt;
		-Dcatalina.home=/bundles/tomcat-6.0.x&lt;br /&gt;
		-Djava.io.tmpdir=/bundles/tomcat-6.0.x/temp&lt;br /&gt;
		-Dexternal-properties=${workspace_loc:&amp;lt;NAME_OF_YOUR_ECLIPSE_PROJECT&amp;gt;}/portal-web/docroot/WEB-INF/src/portal-developer-tomcat.properties&lt;br /&gt;
		-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager&lt;br /&gt;
		-Djava.util.logging.config.file=/bundles/tomcat-6.0.x/conf/logging.properties&lt;/code&gt;&lt;br /&gt;
		&lt;br /&gt;
		You many notice that I&amp;nbsp;included a reference to my portal project's portal-developer-tomcat.properties file. This way I can change portal settings and only need to restart the portal without a redeploy, which is very convenient.&lt;br /&gt;
		&lt;br /&gt;
		You may also notice that I included tomcat's juli log manager so that all the normal web app configurations work as expected and in the Eclipse console, which is also very nice.&lt;br /&gt;
		&lt;br /&gt;
		Adjust your heap settings as desired, but the above settings have worked for me in 99.999% of cases.&lt;br /&gt;
		&amp;nbsp;&lt;/li&gt;
	&lt;li&gt;
		In the Working directory field choose "Other" and enter &lt;code&gt;/bundles/tomcat-6.0.x/bin&lt;/code&gt;. Notice that this is the tomcat path, plus the &lt;code&gt;/bin&lt;/code&gt; folder.&lt;/li&gt;
	&lt;li&gt;
		The JRE&amp;nbsp;tab should be already set properly, but you can choose any JRE that is installed that is 1.5+.&lt;/li&gt;
	&lt;li&gt;
		On the Classpath tab, remove any entries under User Entries and then choose "Add External JARs...".&lt;/li&gt;
	&lt;li&gt;
		Select the 3 jars located in &lt;code&gt;/bundles/tomcat-6.0.x/bin.&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;
		On the Source tab make sure that you have both Default, as well as your project in the list.&lt;br /&gt;
		&lt;br /&gt;
		Optionally here you can add any plugin projects that you are working on, as well as adding the path to the jsp java classes (typically &lt;code&gt;/bundles/tomcat-6.0.x/work/Catalina/localhost/_&lt;/code&gt;)&lt;/li&gt;
	&lt;li&gt;
		On the Common tag choose at least Debug and Run under "Display in favorites menu".&lt;/li&gt;
	&lt;li&gt;
		Click "Apply" near the bottom of the dialog.&lt;/li&gt;
	&lt;li&gt;
		Finally, to start the portal in debug mode, click the Debug menu in the Eclipse toolbar, and choose the run config you jsut created.&lt;br /&gt;
		&lt;br /&gt;
		You should see at this point output on the Eclipse console showing that tomcat is starting up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
	I've been using this techinque for at least 3 years and I run the portal this way 99.999% of the time. By doing so, I&amp;nbsp;can at any point in time add a breakpoint and start trouble shooting and I&amp;nbsp;don't have to worry about re-connecting to or restarting the portal in debug mode, I know it's already done.&lt;br /&gt;
	&lt;br /&gt;
	If you want to add any other java apps this way, the simples technique is to first run the app in the traditional way, then observe the process log to see what the full command that was actually used to start it, and from where. Once you know that, you can add any app to Eclipse Run Configuration.&lt;br /&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
	&amp;nbsp;&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-09-22T14:23:47Z</dc:date>
  </entry>
  <entry>
    <title>Listing Article fragments dynamically using Liferay's WCM Templates</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/listing-article-fragments-dynamically-using-liferay-s-wcm-templates" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/listing-article-fragments-dynamically-using-liferay-s-wcm-templates</id>
    <updated>2010-09-16T14:36:13Z</updated>
    <published>2010-09-16T14:21:34Z</published>
    <summary type="html">&lt;p&gt;&amp;nbsp;So &lt;a href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/tricks-of-velocity-class-loading"&gt;my last blog post&lt;/a&gt; involved a classloader trick for velocity but it also involved a problem of dynamically listing article fragments on a page.&lt;/p&gt; &lt;p&gt;Now, there are a few ways to do this, but the two that come to mind are:&lt;/p&gt; &lt;p&gt;1) Render each article using a specified template which only shows the content of the desired fields (fine, but seems overkill)&lt;/p&gt; &lt;p&gt;2) Parse the article content XML to get only specified bits (also fine, but there are concerns when the content is localized, not to mention isn't the XML handling code ugly/heavy?)&lt;/p&gt; &lt;p&gt;I've mostly taken approach 1), but could approach 2) be clean and light if we do it right? I think it can!&lt;/p&gt; &lt;p&gt;Here is how I did it (using the trick from the last post of course &lt;img alt="" src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/regular_smile.gif" /&gt;):&lt;/p&gt; &lt;table width="200" border="1" cellpadding="1" cellspacing="1" style="background-color: black; color:white;"&gt;     &lt;tbody&gt;         &lt;tr&gt;             &lt;td&gt;&lt;pre&gt;
#set ($journalArticleLocalService = $serviceLocator.findService('com.liferay.portlet.journal.service.JournalArticleLocalService'))
#set ($localeTransformer = $portal.getClass().forName('com.liferay.portlet.journal.util.LocaleTransformerListener').newInstance())
#set ($VOID = $localeTransformer.setLanguageId($request.theme-display.language-id))

#set ($companyId = $getterUtil.getLong($request.theme-display.company-id))
#set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id))

#set ($obc = $portal.getClass().forName(&amp;quot;com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator&amp;quot;).newInstance())

#set ($articles = $journalArticleLocalService.search($companyId, $scopeGroupId, '', null, null, null, null, null, null, 0, null, -1, -1, $obc))

&amp;lt;ul&amp;gt; 
#foreach ($article in $articles)
     #set ($xml = $localeTransformer.onXml($article.content))
     #set ($doc = $saxReaderUtil.read($xml))
     #set ($title = $doc.valueOf(&amp;quot;//dynamic-element[@name='title']/dynamic-content/text()&amp;quot;))
     &amp;lt;li&amp;gt;${title}&amp;lt;/li&amp;gt;
#end
&amp;lt;/ul&amp;gt;&lt;/pre&gt;&lt;/td&gt;         &lt;/tr&gt;     &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now that's contrived, but it's pretty lean, mean and straight to the point, which is always good.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-09-16T14:21:34Z</dc:date>
  </entry>
  <entry>
    <title>Tricks of Velocity class loading</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/tricks-of-velocity-class-loading" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/tricks-of-velocity-class-loading</id>
    <updated>2010-09-16T14:14:51Z</updated>
    <published>2010-09-16T14:00:53Z</published>
    <summary type="html">&lt;p&gt;Recently I was asked to solve a WCM dynamic Template problem which involved listing articles using the business logic of the template.&lt;/p&gt; &lt;p&gt;One of the first problems I encountered was of course doing a search for the Articles via the &lt;code&gt;JournalArticleLocalService&lt;/code&gt; which, in the case of the searchs which return&amp;nbsp;&lt;code&gt;List&amp;lt;JournalArticle&amp;gt;,&lt;/code&gt; require an &lt;code&gt;OrderByComparator&lt;/code&gt;. Since there is no way to directly instantiate classes in Velocity I wondered how I was going to handle this issue.&lt;/p&gt; &lt;p&gt;Luckily my colleague Thiago was working with a community member (&lt;a href="http://www.liferay.com/web/sabrina.locks/profile"&gt;Sabrina Schürhaus Locks&lt;/a&gt;) who had already solved the problem in a rather&lt;span class="Apple-style-span" style="line-height: 17px; font-size: 11.6667px; "&gt;&amp;nbsp;interesting&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: 11.6667px; "&gt;&amp;nbsp;and&lt;/span&gt;&lt;span class="Apple-style-span" style="line-height: 20px; font-size: 11.6667px; "&gt;&amp;nbsp;simple&lt;/span&gt;&lt;span class="Apple-style-span" style="line-height: 17px; font-size: 11.6667px; "&gt;&amp;nbsp;way:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;#set ($obc = $portal.getClass().forName(&amp;quot;com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator&amp;quot;).newInstance())&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;It's so simple I feel silly that I never thought of this myself.&lt;/p&gt;&lt;p&gt;Kudos Sabrina!&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-09-16T14:00:53Z</dc:date>
  </entry>
  <entry>
    <title>Writing Liferay Plugins with Groovy</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/writing-liferay-plugins-with-groovy" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/writing-liferay-plugins-with-groovy</id>
    <updated>2011-02-04T23:34:29Z</updated>
    <published>2010-07-25T04:50:05Z</published>
    <summary type="html">&lt;p&gt;We've been saying for a long time that you could write plugins in a variety of languages, but we never really had any examples to prove that.&lt;/p&gt; &lt;p&gt;Of course some languages will be far simpler to achieve this that others. It'll be far simpler to do this using languages that have native java bindings. Groovy is writen natively in java and even extends the JDK with awesome new features, so that's the one I'm choosing.&lt;/p&gt; &lt;p&gt;Another concern is speed of delievery while not comprimising on performance, stability, ability to debug, etc.&lt;/p&gt; &lt;p&gt;Groovy has awesome tooling support under Eclipse, and even takes part in debugging with little problem at all. All I had to do was install the Groovy Eclipse plugin and add the Groovy nature to my plugin project (yes, I'm using the LIDE. But since this isn't a blog about LIDE, I'm not gonna waste time showing that. It's safe to say that it wasn't a huge effort to add the Groovy support to the .project file and the IDE came in handy when specifying the props configurations which were few).&lt;/p&gt; &lt;p&gt;So, I'm going to demonstrate how to write a hook in Groovy. I want to demonstate 2 things in particular:&lt;/p&gt; &lt;ol&gt;     &lt;li&gt;That you can indeed write Liferay plugins using another language, in this case Groovy.&lt;/li&gt;     &lt;li&gt;That you can use the power of a wonderful language like Groovy to achieve things that would take an order of magnitude more code if you were to try with java.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;I also have three goals:&lt;/p&gt; &lt;ol&gt;     &lt;li&gt;I'm going to implement a single listener of as many model events as I choose, listenting to as many model types as I&amp;nbsp;choose, without having to write more that a single implementation.&lt;/li&gt;     &lt;li&gt;I'm going to persist audit events to the DB using as little persistence code as physically possible.&lt;/li&gt;     &lt;li&gt;I'm going to do it really fast (I'm pretending my boss wanted this done last week...).&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;So, where do I&amp;nbsp;start?&lt;/p&gt; &lt;p&gt;The first thing I&amp;nbsp;need to do is support compiling my groovy code. To do that I'm going to override the default ant target which normally compiles the plugin code, but only for this one plugin (Cause I&amp;nbsp;don't want to harm other projects until I&amp;nbsp;have this nailed down to a science.)&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;First thing to do is download the latest Groovy jar and add that to your plugin's &lt;code&gt;&amp;lt;project&amp;gt;/docroot/WEB-INF/lib&lt;/code&gt; folder (I used groovy-all-1.7.3.jar, which was the latest version at the time of writting).&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;Next, I'll open up &lt;code&gt;&amp;lt;project&amp;gt;/build.xml&lt;/code&gt; and paste the following ant target definition:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;pre&gt;
            &amp;lt;target name=&amp;quot;compile&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;antcall target=&amp;quot;merge&amp;quot; /&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;mkdir dir=&amp;quot;docroot/WEB-INF/classes&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;mkdir dir=&amp;quot;docroot/WEB-INF/lib&amp;quot; /&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;copy todir=&amp;quot;docroot/WEB-INF/lib&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;${app.server.lib.portal.dir}&amp;quot; includes=&amp;quot;${plugin.jars}&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/copy&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;copy todir=&amp;quot;docroot/WEB-INF/tld&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;${app.server.portal.dir}/WEB-INF/tld&amp;quot; includes=&amp;quot;${plugin.tlds}&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/copy&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;if&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;available file=&amp;quot;docroot/WEB-INF/src&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;if&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;available file=&amp;quot;tmp&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;path id=&amp;quot;plugin-lib.classpath&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;docroot/WEB-INF/lib&amp;quot; includes=&amp;quot;*.jar&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;tmp/WEB-INF/lib&amp;quot; includes=&amp;quot;*.jar&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pathelement location=&amp;quot;docroot/WEB-INF/classes&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pathelement location=&amp;quot;tmp/WEB-INF/classes&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/path&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;else&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;path id=&amp;quot;plugin-lib.classpath&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;docroot/WEB-INF/lib&amp;quot; includes=&amp;quot;*.jar&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pathelement location=&amp;quot;docroot/WEB-INF/classes&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/path&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/else&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/if&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;copy todir=&amp;quot;docroot/WEB-INF/lib&amp;quot;&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fileset dir=&amp;quot;${app.server.lib.portal.dir}&amp;quot; includes=&amp;quot;${required.portal.jars}&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/copy&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;if&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;available file=&amp;quot;docroot/WEB-INF/lib/portal-impl.jar&amp;quot; /&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;fail&amp;gt;
            .
            
            Detected inclusion of portal-impl.jar in WEB-INF/lib.
            
            portal-impl.jar is designed with a large number of singleton classes which are
            instantiated on the basis that they will exist alone in the application server.
            
            While compile time issues may be resolved, portlets cannot be made to work by
            simply adding portal-impl.jar, because doing so violates the above assumption,
            and the resulting problems will be extremely difficult to debug.
            
            Please find a solution that does not require portal-impl.jar.
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/fail&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/if&amp;gt;
            
            &lt;span style="color: red;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;taskdef name=&amp;quot;groovyc&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classname=&amp;quot;org.codehaus.groovy.ant.Groovyc&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classpathref=&amp;quot;plugin.classpath&amp;quot;/&amp;gt;&lt;br /&gt;            &lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groovyc&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classpathref=&amp;quot;plugin.classpath&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; destdir=&amp;quot;docroot/WEB-INF/classes&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; srcdir=&amp;quot;docroot/WEB-INF/src&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;gt;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;javac&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; compiler=&amp;quot;${javac.compiler}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; debug=&amp;quot;${javac.debug}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; deprecation=&amp;quot;${javac.deprecation}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; fork=&amp;quot;${javac.fork}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; memoryMaximumSize=&amp;quot;${javac.memoryMaximumSize}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nowarn=&amp;quot;${javac.nowarn}&amp;quot;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; /&amp;gt;&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/groovyc&amp;gt;&lt;/span&gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/then&amp;gt;
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/if&amp;gt;
            
            &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;antcall target=&amp;quot;merge&amp;quot; /&amp;gt;
            &amp;lt;/target&amp;gt;&lt;/pre&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;p&gt;&amp;nbsp;&lt;br /&gt;     Note the part in red that replaces the default compiler call with one to &lt;code&gt;groovyc&lt;/code&gt; (Don't worry, this will also compile any java code seamlessly if there is some in the project).&lt;br /&gt;     &amp;nbsp;&lt;/p&gt;&lt;/li&gt;     &lt;li&gt;Now I&amp;nbsp;make sure that my hook has a properties file defined cause I'm going to write an application Startup event, and a model listener:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&amp;lt;hook&amp;gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;portal-properties&amp;gt;portal.properties&amp;lt;/portal-properties&amp;gt;&lt;br /&gt;                 &amp;lt;/hook&amp;gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;My two implementations are going to be called &lt;code&gt;com.liferay.sample.groovy.GStartupAction&lt;/code&gt; and &lt;code&gt;com.liferay.sample.groovy.GModelListener&lt;/code&gt; so I'll set those up in the props file (for now I'm just picking a whole bunch of interesting models to listen to, I&amp;nbsp;can tune this again later):&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;application.startup.events=com.liferay.sample.groovy.GStartupAction&lt;br /&gt;                 &lt;br /&gt;                 value.object.listener.com.liferay.portal.model.User=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portal.model.Layout=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.blogs.model.BlogsEntry=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.blogs.model.BlogsStatsUser=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.journal.model.JournalArticle=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.journal.model.JournalStructure=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.journal.model.JournalTemplate=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.messageboards.model.MBCategory=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.messageboards.model.MBMessage=com.liferay.sample.groovy.GModelListener&lt;br /&gt;                 value.object.listener.com.liferay.portlet.messageboards.model.MBThread=com.liferay.sample.groovy.GModelListener&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;Ok, so now we have all the house keeping stuff done, and I&amp;nbsp;can get to work on writting the code. For the sake of speed, I'm using the simplest type of DB interaction that comes in the form of Groovy's &lt;code&gt;groovy.sql.Sql&lt;/code&gt; class which easily lets me do JDBC operations using very lean amount of code (my DB operations are on MySQL specifically, again for the sake of speed, I'm not concerned with syntax that will work with other DBs). &lt;br /&gt;     &lt;br /&gt;     The first thing I&amp;nbsp;need to do is create the DB table, if it doesn't exist. I'm going to do that in my GStartupAction:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;package com.liferay.sample.groovy&lt;br /&gt;                 &lt;br /&gt;                 import com.liferay.portal.kernel.events.SimpleAction&lt;br /&gt;                 import com.liferay.portal.kernel.util.InfrastructureUtil&lt;br /&gt;                 &lt;br /&gt;                 import groovy.sql.Sql&lt;br /&gt;                 &lt;br /&gt;                 class GStartupAction extends SimpleAction {&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;public void run(String[] arg0) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;try {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;_sql.rows('select count(*) from AuditLog')&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;catch (e) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;_sql.execute '''&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;create table AuditLog (&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditId bigint not null primary key,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId bigint,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;className varchar(75),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classPK bigint,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classUuid varchar(75),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditDate datetime,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;description varchar(200),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;model longtext&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;'''&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;private static final Sql _sql = new Sql(InfrastructureUtil.dataSource)&lt;br /&gt;                 &lt;br /&gt;                 }&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &amp;nbsp;You gotta admit that's pretty short. That's it for that class.&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;&amp;nbsp;On to GModelListener. Note that we want it to work with any of the models we throw at it. And we also want to limit the code, so we're going to extend &lt;code&gt;com.liferay.portal.model.BaseModelListener&amp;lt;T&amp;gt;&lt;/code&gt;. The only events we care about are: onBeforeCreate, onBeforeRemove, onBeforeUpdate.&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;package com.liferay.sample.groovy&lt;br /&gt;                 &lt;br /&gt;                 import com.liferay.portal.model.BaseModelListener&lt;br /&gt;                 &lt;br /&gt;                 class GModelListener extends BaseModelListener {&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeCreate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeRemove(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeUpdate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 }&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     We start with the above.&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;I need a couple other objects setup, logging, and the Groovy Sql object that I need to do the DB operations:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;package com.liferay.sample.groovy&lt;br /&gt;                 &lt;br /&gt;                 import com.liferay.portal.kernel.log.LogFactoryUtil&lt;br /&gt;                 import com.liferay.portal.kernel.util.InfrastructureUtil;&lt;br /&gt;                 import com.liferay.portal.model.BaseModelListener&lt;br /&gt;                 &lt;br /&gt;                 class GModelListener extends BaseModelListener {&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeCreate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeRemove(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeUpdate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _log = LogFactoryUtil.getLog(GModelListener.class)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _sql = new Sql(InfrastructureUtil.dataSource)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _auditLog = _sql.dataSet(&amp;quot;AuditLog&amp;quot;)&lt;br /&gt;                 &lt;br /&gt;                 }&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     The &lt;code&gt;_auditLog&lt;/code&gt; variable is a Groovy &lt;code&gt;groovy.sql.DataSet&lt;/code&gt; object that lets me do really clean operations on a given table.&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;Finally, I&amp;nbsp;want to write a &lt;code&gt;closure&lt;/code&gt; that will do the work of updating the AuditLog table, but first I&amp;nbsp;want a very simple API for it:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;package com.liferay.sample.groovy&lt;br /&gt;                 &lt;br /&gt;                 import com.liferay.portal.kernel.log.LogFactoryUtil&lt;br /&gt;                 import com.liferay.portal.kernel.util.InfrastructureUtil;&lt;br /&gt;                 import com.liferay.portal.model.BaseModelListener&lt;br /&gt;                 &lt;br /&gt;                 class GModelListener extends BaseModelListener {&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;void onBeforeCreate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; audit(model, &amp;quot;onBeforeCreate&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; void onBeforeRemove(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;audit(model, &amp;quot;onBeforeRemove&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; void onBeforeUpdate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;audit(model, &amp;quot;onBeforeUpdate&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;br /&gt;                 &lt;span style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // do my work here&lt;/span&gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _log = LogFactoryUtil.getLog(GModelListener.class)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _sql = new Sql(InfrastructureUtil.dataSource)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _auditLog = _sql.dataSet(&amp;quot;AuditLog&amp;quot;)&lt;br /&gt;                 &lt;br /&gt;                 }&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     That's pretty straight forward! It'll do the trick.&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;Now, I want to track who did the particular operation, so I need to current user's Id, so it can be recoreded. The simplest way to do this is to get the current PermissionChecker and if it exists, then record the userId:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;We also want the Group, anbd for the sake of completeness, if the model has a uuid field we want that too:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;/p&gt;                 &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def groupId = 0&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def uuid = ''&lt;br /&gt;                 &lt;/span&gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     But, how do we handle with the models without those fields? Isn't there a bunch of reflection involed in handling things like that? Well, in java, Yes! In Groovy, No! This is so easy in Groovy it's almost trivial:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;/p&gt;                 &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def groupId = 0&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def uuid = ''&lt;br /&gt;                 &lt;br /&gt;                 &lt;span style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getGroupId')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId = model.groupId&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getUuid')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;uuid = model.uuid&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     That's to say, if the model object has a given method, we'll just call it. If not, well we just ignore it and keep the default value.&lt;/li&gt;     &lt;li&gt;Next we'll populate a map of data we want to store in the AuditLog table:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;/p&gt;                 &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def groupId = 0&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def uuid = ''&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getGroupId')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId = model.groupId&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getUuid')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;uuid = model.uuid&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;def map = [&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditId: CounterLocalServiceUtil.increment(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId: groupId,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;className: model.class.name,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classPK: model.primaryKey,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classUuid: uuid,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditDate: new Date(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;description: message + &amp;quot; by &amp;quot; + String.valueOf(userId),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;model: model.toString()&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;]&lt;/span&gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     Simple enough!&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;Now, let's store and log the result:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;/p&gt;                 &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def groupId = 0&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def uuid = ''&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getGroupId')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId = model.groupId&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;if (model.metaClass.respondsTo(model, 'getUuid')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;uuid = model.uuid&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;def map = [&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditId: CounterLocalServiceUtil.increment(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;groupId: groupId,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;className: model.class.name,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classPK: model.primaryKey,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classUuid: uuid,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditDate: new Date(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;description: message + &amp;quot; by &amp;quot; + String.valueOf(userId),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;model: model.toString()&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;]&lt;br /&gt;                 &lt;br /&gt;                 &lt;span style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;_auditLog.add(&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;auditId: map.auditId, groupId: map.groupId,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;className: map.className, classPK: map.classPK,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;classUuid: map.classUuid, auditDate: map.auditDate,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;description: map.description, model: map.model)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;if (_log.infoEnabled) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;_log.info map&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;                 &lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     Phew!!! We're done. We got it all done before lunch time.&lt;br /&gt;     &lt;br /&gt;     Arguably, we could have saved another 13 lines od code if we didn't need the map that we used for both persisting and logging the event. We could just as easily passed the raw values to the _auditLog instance, but wait.. we don't want the code to be TOO short, otherwise our &amp;quot;lines of code contributed&amp;quot; factor will make it look like we never do ANY work at all!!!&lt;br /&gt;     &amp;nbsp;&lt;/li&gt;     &lt;li&gt;The whole class looks like this:&lt;br /&gt;     &lt;table width="200" cellspacing="1" cellpadding="1" border="1" style="background-color: black; color: white; font-family: monospace;"&gt;         &lt;tbody&gt;             &lt;tr&gt;                 &lt;td&gt;package com.liferay.sample.groovy&lt;br /&gt;                 &lt;br /&gt;                 import com.liferay.counter.service.CounterLocalServiceUtil&lt;br /&gt;                 import com.liferay.portal.kernel.log.LogFactoryUtil&lt;br /&gt;                 import com.liferay.portal.kernel.util.InfrastructureUtil;&lt;br /&gt;                 import com.liferay.portal.model.BaseModelListener&lt;br /&gt;                 import com.liferay.portal.security.permission.PermissionThreadLocal;&lt;br /&gt;                 &lt;br /&gt;                 import groovy.sql.Sql&lt;br /&gt;                 &lt;br /&gt;                 class GModelListener extends BaseModelListener {&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; void onBeforeCreate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; audit(model, &amp;quot;onBeforeCreate&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; void onBeforeRemove(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; audit(model, &amp;quot;onBeforeRemove&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; void onBeforeUpdate(model) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; audit(model, &amp;quot;onBeforeUpdate&amp;quot;)&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; def audit = { model, message -&amp;gt;&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def userId = PermissionThreadLocal.permissionChecker?.userId&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def groupId = 0&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def uuid = ''&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (model.metaClass.respondsTo(model, 'getGroupId')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; groupId = model.groupId&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (model.metaClass.respondsTo(model, 'getUuid')) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; uuid = model.uuid&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; def map = [&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; auditId: CounterLocalServiceUtil.increment(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; groupId: groupId,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; className: model.class.name,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classPK: model.primaryKey,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classUuid: uuid,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; auditDate: new Date(),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; description: message + &amp;quot; by &amp;quot; + String.valueOf(userId),&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; model: model.toString()&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ]&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; _auditLog.add(&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; auditId: map.auditId, groupId: map.groupId,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; className: map.className, classPK: map.classPK,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classUuid: map.classUuid, auditDate: map.auditDate,&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; description: map.description, model: map.model)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (_log.infoEnabled) {&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; _log.info map&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _log = LogFactoryUtil.getLog(GModelListener.class)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _sql = new Sql(InfrastructureUtil.dataSource)&lt;br /&gt;                 &lt;br /&gt;                 &amp;nbsp;&amp;nbsp;&amp;nbsp; private static final _auditLog = _sql.dataSet(&amp;quot;AuditLog&amp;quot;)&lt;br /&gt;                 &lt;br /&gt;                 }&lt;/td&gt;             &lt;/tr&gt;         &lt;/tbody&gt;     &lt;/table&gt;     &lt;br /&gt;     Now you can go back and add the missing models and you're good to go.&lt;br /&gt;     &lt;br /&gt;     Oh, and when you're done, take the rest of the day off... you deserve it!&lt;/li&gt; &lt;/ul&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-07-25T04:50:05Z</dc:date>
  </entry>
  <entry>
    <title>Liferay Staging 6.0</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-staging-6-0" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/liferay-staging-6-0</id>
    <updated>2010-07-15T21:50:32Z</updated>
    <published>2010-07-15T21:44:04Z</published>
    <summary type="html">&lt;p&gt;Some of the latest details about Liferay Staging in 6.0 are now on the Wiki:&amp;nbsp;&lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Staging+-+6.0/maximized"&gt;Staging - 6.0&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Local Live&lt;/li&gt;&lt;li&gt;Remote Live&lt;/li&gt;&lt;li&gt;Better Differential Publishing&lt;/li&gt;&lt;li&gt;Stored connections settings for remote staging (as you'd expect)&lt;/li&gt;&lt;li&gt;Un-Staged Portlets (pick and choose which portlets are staged)&lt;/li&gt;&lt;li&gt;Lots and Lots of fixes and usability improvements&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let us know what you think about it.&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-07-15T21:44:04Z</dc:date>
  </entry>
  <entry>
    <title>Ideas for Blog and WCS talks</title>
    <link rel="alternate" href="http://www.liferay.com/web/raymond.auge/blog/-/blogs/ideas-for-blog-and-wcs-talks" />
    <author>
      <name>Ray Augé</name>
    </author>
    <id>http://www.liferay.com/web/raymond.auge/blog/-/blogs/ideas-for-blog-and-wcs-talks</id>
    <updated>2010-06-24T13:39:10Z</updated>
    <published>2010-06-22T21:57:10Z</published>
    <summary type="html">&lt;p&gt;I haven't been blogging as much as I'd like...&lt;/p&gt;&lt;p&gt;My return to core after a long year+ on a project hasn't turned out quite the way I had envisioned. &lt;img alt="" src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/wink_smile.gif" /&gt;&amp;nbsp;(Don't get me wrong, I'm SOOOO happy to be back; although, to be fair I was with an awesome client.)&lt;/p&gt;&lt;p&gt;I had two particular things in mind when coming back:&lt;/p&gt;&lt;p&gt;1) lots of new code to play with&lt;br /&gt;2) lots of blogging about all things Liferay&lt;/p&gt;&lt;p&gt;Part&amp;nbsp;1)&amp;nbsp;&lt;em&gt;&amp;quot;lots of new code to play with&amp;quot;&lt;/em&gt;&amp;nbsp;has turned out as I expected, and more. So much code.... &lt;img alt="" src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/cry_smile.gif" /&gt;&amp;nbsp;&amp;nbsp;The sad thing is that it also has gotten in the way of 2) &lt;em&gt;&amp;quot;lots of blogging about all things Liferay&amp;quot;&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Really, there are so many new features and refinements in Liferay 6.0 that it's mind boggling to think of how much innovation has taken place, and I missed so much of it. I frankly sometimes don't know where to start.&lt;/p&gt;&lt;p&gt;In fact, I was so mind boggled the weeks after being back that during lead up to ECS I got so confused over what and who was doing what that I ended up not having a single talk about code. And to be honest, I should never speak publicly about anything except code. Thankfully Brett saved me and did a talk in my place. (Thanks Brett! I know it wasn't all a win, win for you.)&lt;/p&gt;&lt;p&gt;Anyhow, I promise that &lt;a href="http://www.liferay.com/about-us/events/liferay-symposiums/west-coast-2010/general"&gt;WCS&lt;/a&gt; will be a different story. I will speak about nothing but code. In fact, I will actually speak IN code in order to make up for the missed opportunity at ECS &lt;img alt="" src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/whatchutalkingabout_smile.gif" /&gt;. (Just kidding!)&lt;/p&gt;&lt;p&gt;No but honestly, I want to know what would be the most compelling topic(s) for me to discuss at &lt;a href="http://www.liferay.com/about-us/events/liferay-symposiums/west-coast-2010/general"&gt;WCS&lt;/a&gt;. I have several ideas; some may turn up as blog posts, but some could be specially for &lt;a href="http://www.liferay.com/about-us/events/liferay-symposiums/west-coast-2010/general"&gt;WCS&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I'm still planning to write about groovy|ruby plugins in the near future. I could always talk about the latest way to RAD on Liferay (which is always of interest to me personally).&lt;/p&gt;&lt;p&gt;But hey, if you're planning to be at &lt;a href="http://www.liferay.com/about-us/events/liferay-symposiums/west-coast-2010/general"&gt;WCS&lt;/a&gt;, and you have a topic that you'd like me to talk about, throw it up here and I'll see if we can work it in.&lt;/p&gt;&lt;p&gt;Whatever the topic, I can try to speak a little on it whether I do a full blog on it or not (that is, unless I know who can give a better answer), so don't hold back (too much)!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Ideas?&lt;/p&gt;</summary>
    <dc:creator>Ray Augé</dc:creator>
    <dc:date>2010-06-22T21:57:10Z</dc:date>
  </entry>
</feed>


