Recent Bloggers

Gwowen Fu

1 Mensagens
24 de Maio de 2013

Joshua Asbury

Staff
21 Mensagens
23 de Maio de 2013

Liferay Support Portal Admin

Staff
57 Mensagens
23 de Maio de 2013

Navin Agarwal

2 Mensagens
22 de Maio de 2013

Olaf Kock

Staff
51 Mensagens
21 de Maio de 2013

Ronald Sarayudej

Staff
149 Mensagens
20 de Maio de 2013

Roberto Díaz

Staff
1 Mensagens
16 de Maio de 2013

Miguel Pastor Olivar

Staff
8 Mensagens
14 de Maio de 2013

Kamesh Sampath

5 Mensagens
13 de Maio de 2013

Richard Sezov

Staff
38 Mensagens
13 de Maio de 2013
« Voltar

Custom Finder for Portal Entities in a Plugin

Staff Blogs 22 de Junho de 2012 Por David Truong Staff

Sometimes you need to write a custom finder for a table that belongs to core. In my case it was JournalArticle.  My client wanted to avoid using the ext-plugin as much as possible so I had to try to figure out a way to do it within a portlet plugin.

I tried to use dynamic queries as a solution but it turned out to be inadequate because I was unable to group by (I wanted to get the latest articles with a certain structureId).

I needed a custom finder but for awhile I thought this was impossible because you needed to load the Impl of a model during the query which is inaccessible since it is part of portal-impl.  But my experimentation with dynamic queries was not in vain because I figured... if dynamic queries can figure out a way to load the impl class I could probably do the same.

So here is the class I wrote.

It consists of two basic methods:


public static Class getImplClass(Class clazz, ClassLoader classLoader) {
    if (!clazz.getName().endsWith("Impl")) {
        String implClassName = clazz.getPackage().getName() + ".impl." + 
            clazz.getSimpleName() + "Impl";

       	clazz = _classMap.get(implClassName);

       	if (clazz == null) {    
            try {
               			if (classLoader == null) {   
                    Thread currentThread = Thread.currentThread();

                    classLoader = currentThread.getContextClassLoader(); 
                }

                clazz = classLoader.loadClass(implClassName); 
               _classMap.put(implClassName, clazz); 
            }
            catch (Exception e) {
                _log.error("Unable find model " + implClassName, e);
            }
        } 
    }
    return clazz;
}
	
public static Session openPortalSession() throws ORMException {
    return sessionFactory.openSession();	
}

private static Log _log = LogFactoryUtil.getLog(CustomFinderHelperUtil.class);

private static Map> _classMap = new HashMap>();
	
private static SessionFactory sessionFactory =
    (SessionFactory)PortalBeanLocatorUtil.locate("liferaySessionFactory");

So basically in my customFinder instead of doing

session = openSession();

I did

session = CustomFinderHelperUtil.openPortalSession();

and instead of doing

q.addEntity("JournalArticle",  JournalArticleImpl.class));

I did

q.addEntity("JournalArticle", CustomFinderHelperUtil.getImplClass(
                JournalArticle.class, true));

(I created a extra method that took a boolean to use the portalClassLoader, you can load it using PortalClassLoaderUtil.getPortalClassLoader())

Hopefully this helps some you guys out.

 

Respostas do tópico Autor Data
very cool, now it all makes sense. Wilson Man 22 de Junho de 2012 13:10
May be the SQLQuery(Impl) could be extended so... Sampsa Sohlman 23 de Junho de 2012 15:24
NIce! 다니엘 Daniel Bastos 28 de Junho de 2012 07:42
FYI You are using _classMap in a non ... Jelmer Kuperus 9 de Julho de 2012 02:55
Nice Post... Jay Patel 10 de Julho de 2012 07:41
Hey jelmer, Good points. I will update my... David Truong 10 de Julho de 2012 12:59
Hey Sampsa, That would make things too easy... David Truong 10 de Julho de 2012 13:01
It is a bit challenging to add a new action... Muhammed Shakir AK Misarwala 7 de Outubro de 2012 03:09

very cool, now it all makes sense.
Postado em 22/06/12 13:10.
May be the SQLQuery(Impl) could be extended so that if there is entity's interface as parameter then, it would find the implementation automatically.

Anyway, good post.
Postado em 23/06/12 15:24.
Postado em 28/06/12 07:42.
FYI

You are using _classMap in a non threadsafe way

Also by holding on to the impl class references in a map you are effectively preventing the webapp classloader from being garbage collected. (not a problem if you just use it to load classes from the ROOT webapp, since it's not hotdeployable, but it is if you use this same trick do load classes from another standalone portlet) You'll be blowing up your permgen space
Postado em 09/07/12 02:55.
Postado em 10/07/12 07:41.
Hey jelmer,

Good points. I will update my code so that if the classLoader is not ROOT it won't put it in the map. You should consider filing a jira issue also because this code is a copy of what is from DynamicFactoryUtil

Thanks for your comments guys
Postado em 10/07/12 12:59 em resposta a Jay Patel.
Hey Sampsa,

That would make things too easy for you guys then! Haha JK. Good suggestion. It is even more valid because the look up is already being done for dynamic queries. Very little code would have to be written for this improvement.
Postado em 10/07/12 13:01 em resposta a David Truong.
It is a bit challenging to add a new action with a custom jsp and ensuring that the jsp is wrapped into the theme properly. I have got the solution and it works like a charm. You can find the solution here : http://www.liferayaddict.com/home/-/blogs/new-action-in-hook-in-liferay-6-1
Postado em 07/10/12 03:09 em resposta a David Truong.