Hooks revisited (0) - the TL;TR

Technical Blogs April 28, 2014 By Peter Breuer

A new domain for hook plug-ins

The hook technology is applied to modeling and extending of core meta-data. A generic development method is suggested in a series of blog articles. In particular, it is described, how the Asset Publisher portlet preferences can be extended by additional filter conditions on widely available meta-data. Other extensions are straighforward. The method makes use of the finder architecture and requires very little coding effort. However the development process would be more transparent with direct infrastructure support.


The method advocates a more flexible architecture related to meta-data. The referenced plug-in has been implemented and fully tested in a real-life envronment. Please find a related analysis about finders and search methods here.


Hooks revisited (1) - model architecture

Technical Blogs April 28, 2014 By Peter Breuer

Persisted queries

Entity models and entity persistence are built on a well-established hierarchy of interfaces. For each specific entity name <ENTITY_> we have the model interfaces:



Interfaces are generated from the entity description by the Service Builder, along with the corresponding implementation classes:



Not all persistence classes of the framework are created by the Service Builder though. Dozens of persistence classes exist in the core without explicitly having declared them as models. Their existence is hardly ever noticed because they are somewhat hidden by design. Examples are search expressions, filter rules, or sets of processing parameters that can be stored as portlet preferences. This class category does not even have a proper name. However, according to their role they might well be called persisted queries or query parameters. In several cases they appear as arguments in finder methods. The AssetEntryQuery class will be used to demonstrate their importance.


In technical terms these are application meta-data which are implemented by POJO persistence classes. They have no model, no interface definition and no service wrapper. Without a proper interface they can hardly be injected into generic persistence methods. The application meta-data are probably regarded as insufficiently structured for having a proper data model. This is a design decision which is by no means a necessity.


So how are these "orphaned" classes actually persisted? As a rule, they are stored as XML text in the database. The framework uses specific implementation-only classes for XML mapping that are not visible outside of the portal application context, i.e. not available for any custom plug-ins. Possibilities of extending those "meta-data models" in a custom hook will be discussed in the sequel.


Hooks revisited (2) - extending the AssetPublisher preferences

Technical Blogs April 28, 2014 By Peter Breuer

The AssetEntryQuery class

Note: please find an UML class diagram of the referenced classes in the DL.


The Asset Publisher portlet is used for rendering blog entries, wiki pages, web articles, and all kinds of other assets stored in the portal framework. It can be customized in order to render only a subset of assets in a given sort order. That part of the portlet preferences are represented by the AssetEntryQuery bean that can be regarded as a model implementation class. Its members hold standard filter and sort criteria that are eventually transformed into SQL WHERE conditions and a list of ORDER BY columns. The AssetEntryQuery bean itself is never persisted. Its primary use is as follows:


public interface AssetEntryFinder

public List<AssetEntry>

findEntries(AssetEntryQuery entryQuery)

throws SystemException;



The method above is implemented in the AssetEntryFinderImpl service bean. It will be located by the generic bean lookup service and called within the AssetEntryLocalServiceImpl class. In particular, the list of filtered and sorted asset entries are returned by the method


public List<AssetEntry> getEntries(AssetEntryQuery entryQuery)


Now suppose that we want to render only web articles authored by a specific user. In the current architecture this is only feasible if the argument AssetEntryQuery included “author” among the search criteria. Unfortunately that is not the case and we are reluctant to make use of an Ext plug-in.


It might be interesting to mention that the portal version 6.0 used to include some 20 members holding search criteria in the AssetEntryQuery class. Later it was increased to 25 members in the version 6.1, and over 30 members in the version 6.2. Apparently each new release requires to cover an increasing number of search criteria. All those criteria member definitions seem to be reasonable, yet, to a great deal, they are also arbitrary. Apparently, a more generic approach would be beneficial.


Let me briefly wrap up, what is the issue here:

  • Assets' meta-data can be extended in different ways, e.g. by Expando Bridge, or Article Structure. The extension is fully supported by the architecture and the core technology.

  • The Asset Publisher portlet preferences covers only a limited choice of asset meta-data for filtering and sorting. Custom extensions are currently not supported in a Hook plug-in.

  • There is a gap between the availability and the usability of assets' meta-data.

How can we attempt to fill the gap? Please read further.


Hooks revisited (3) - overriding finders in the core

Technical Blogs April 28, 2014 By Peter Breuer

Technical requirements

In order to use custom filters / sorting in the Asset Publisher portlet, we want to override the current finder implementation in a hook. Let's consider, what can be achieved within the scope of the current architecture. Here are the requirements for customization:


  1. The portlet preferences should accommodate additional filtering, sorting and rendering parameters. As an example we will use the parameter “author” that should match the asset's owner.

  2. All custom parameters should be persisted together with the currently available standard parameters. The combination of the parameters "asset type" and "author" will enable that, for example, all wiki articles of an author can be selected.

  3. The complete set of search criteria should be implemented as a new bean class, for example CustomAssetEntryQuery. Ideally this would be an extension of the AssetEntryQuery which currently holds the standard search criteria.

  4. The AssetEntryFinderImpl  class should be extended, for example by CustomAssetEntryFinderImpl, that makes use of the new CustomAssetEntryQuery bean as an argument in its methods.

  5. Finally, the AssetEntryLocalServiceImpl should be extended in such a way that the extended CustomAssetEntryFinderImpl will return the required target list of asset entries.

Changing the view component

The first two requirements can easily be fulfilled. Requirement a) can be achieved by a hook plug-in which replaces some Java Server Pages in /html/portlet/asset_publisher/. For example something like:


<aui:select label="author" name="preferences—authorName--">


<optgroup label="<liferay-ui:message key="author" />"





will need to be added in configuration_dynamic.jspf. This will include a new dropdown list in the preferences form of the portlet. Later we will have to read the extended portlet preferences and instantiate the CustomAssetEntryQuery bean in init.jsp. The rendering will need to be adapted in view_dynamic_list.jspf.


Requirement b) happens to have been already covered by the framework's AssetPublisherUtil class. In the next segment we will focus on the requirement c).


Hooks revisited (4) - create an entity to hold seach criteria

Technical Blogs April 28, 2014 By Peter Breuer

Create the CustomAssetEntryQuery entity

We want to extend the AssetEntryQuery by adding the String author to the members. This can be done manually with some coding effort. However the use of Service Builder will offer some additional advantages.


<service-builder package-path="...">


<entity name="CustomAssetEntryQuery"

local-service="false" remote-service="false"


<column name="authorName" type="String" primary="true"




The Service Builder creates, among others, the interface CustomAssetEntryQuery, and the class CustomAssetEntryQueryWrapper, with the ubiquitous get and set methods for the authorName. Now we will need to break well-established rules and manually modify the generated class as follows:


public class CustomAssetEntryQueryWrapper

extends AssetEntryQuery

implements CustomAssetEntryQuery,

ModelWrapper<CustomAssetEntryQuery> {

public CustomAssetEntryQueryWrapper(

CustomAssetEntryQuery customAssetEntryQuery) {

super((AssetEntryQuery) customAssetEntryQuery);

_customAssetEntryQuery = customAssetEntryQuery;





Thus we have a class which implements our new custom model interface and also extends the core class AssetEntryQuery, as required in c). Poking around generated code is not nice, of course. It would be way easier if the meta-data had an extensible model. Anyway, the generated class would be overwritten on the next run, so we need to make a backup for further use. Just one thing we should not play with: never rename the generated classes.


In the next segment we will make use of the meta-data extension in a finder. Please read further.


Hooks revisited (5) - create access to the meta-data model

Technical Blogs April 28, 2014 By Peter Breuer

Create a service jar on the global path

We need to build and deploy our new Hook plug-in which was created in the previous segment. Usually we would make use of the Liferay IDE ant build files to start the Service Builder. This would compile the generated code, build and deploy the access services library jar for our new entity in the lib folder of the plug-in package.


Unfortunately that would not do this time. For one thing, we will need to include the modified wrapper class in the library jar, not the one generated by the Service Builder. But there is more than that. Normally, the access services library is part of the plug-in package and will be deployed in its own application context. So the access services are not visible from the portal application where we would actually need them. Please remember that the Asset Publisher portlet is part of the framework's core.


So we need a little hack in the supplied build file. First, we separate the code generation of the Service Builder from the actual build of the service library jar. Second, we deploy the library jar file somewhere on the global classpath, e.g. in the application server's lib/ext folder.


Now let's make a quick inventory, what we have done until now:

  1. modified parts of the view component, i.e. some jsp(f) files

  2. created an entity description with the authorName attribute

  3. generated the entity's model and persistence interfaces, their implementation classes and manipulated the model wrapper

  4. built the access service library jar and deployed it on the global classpath


Please note that all the code above will be executed in the portal application's context. In the next section we will create the custom finder implementation in the Hook plug-in.


Hooks revisited (6) - create a custom finder

Technical Blogs April 28, 2014 By Peter Breuer

Override AssetEntryQueryFinderImpl

Define a finder interface as follows:


public interface CustomAssetEntryQueryFinder {

public List<AssetEntry> findEntries(

CustomAssetEntryQueryWrapper customEntryQuery)

throws SystemException;



Subsequently create an implementation of the interface:


public class CustomAssetEntryQueryFinderImpl

extends BasePersistenceImpl<CustomAssetEntryQuery>

implements CustomAssetEntryQueryFinder {



public List<AssetEntry> findEntries(

CustomAssetEntryQueryWrapper entryQuery)

throws SystemException;





As a template for the custom finder class we can use the unmodified source of the framework's finder implementation class AssetEntryFinderImpl. Actually we will need to integrate an additional, however optional, SQL fragment into the existing code. The finder method will look something like this:



public List<AssetEntry> findEntries(

CustomAssetEntryQueryWrapper entryQuery)

throws SystemException {

// fetch liferay's session factory

SessionFactory sessionFactory =


Session session = null;

try {

// open session using liferay's session factory

session = sessionFactory.openSession();

SQLQuery q = buildAssetQuerySQL(

entryQuery, false, session);

return (List<AssetEntry>)QueryUtil.list(

q, getDialect(),




catch (Exception e) {

throw new SystemException(e);


finally {





Please note that the custom finder implementation will be loaded by the plug-in context loader due to the simple fact that, in contrast with the access service classes, it will be part of the plug-in package war. The framework's original finder, on the other hand, is loaded by the portal's context loader. This difference must be reflected in the coding. We need the portal's SessionFactory to create the database session because the referenced core tables are not known for the plug-in's default SessionFactory. Similarly, we need to access the portal properties through the PropsUtil class instead of accessing them directly as in the original finder implementation. However, these are only minor technical differences; the custom finder implementation will still remain very close to the template.


The complete SQL statement will be compiled from all the search criteria supplied by the entryQuery argument of the method:


protected SQLQuery buildAssetQuerySQL(

CustomAssetEntryQueryWrapper entryQuery,

Session session)

throws SystemException {


// append join condition

if (entryQuery.getAuthorName().length() > 0) {

sb.append("INNER JOIN ");

sb.append("User_ ON ");

sb.append("(AssetEntry.userID = User_.userId) ");



// append where condition

if (entryQuery.getAuthorName().length() > 0) {

sb.append(" AND (User_.screenname = ?)");



// set positional bind variable

if (entryQuery.getAuthorName().length() > 0) {





We need to regenerate the implementation part of the plug-in in order to add the finder to the entity. This works like a charm thanks to the Service Builder and our effort of splitting the build job into several build targets. Thus we were able to fulfill requirement d).


Hooks revisited (7) - put the custom finder in place

Technical Blogs April 28, 2014 By Peter Breuer

Override the AssetEntry implementation

It remains to show that requirement e) can be met as well. Actually this part is already supported by existing standard development methods. We need to create a Service Hook in our plug-in package. In the liferay-hook.xml file include the override description:











Subsequently create the custom implementation as follows:


public class CustomAssetEntryLocalServiceImpl

extends AssetEntryLocalServiceWrapper {

public CustomAssetEntryLocalServiceImpl(

AssetEntryLocalService assetEntryLocalService) {





public List<AssetEntry> getEntries(AssetEntryQuery entryQuery)

throws SystemException {

if (entryQuery instanceof CustomAssetEntryQueryWrapper) {



(CustomAssetEntryQueryWrapper) entryQuery);


else {

return super.getEntries(entryQuery);






Now we can use the framework's AssetEntryLocalServiceUtil.getEntries(AssetEntryQuery) method to retrieve the asset entries matching the extended search criteria. If the parameter happens to belong to the custom subclass then the custom search criteria, if any, will be effective.


Ideally we should simply inject the finder class into the above service implementation. Unfortunately this is where the current framework version is painfully restrictive. A bit more flexibility in the AssetEntry interface would help. Well, we might have gone up to the wall this time.

Showing 8 results.
Items 20
of 1