« Indietro

Custom Finder for Portal Entities in a Plugin

Company Blogs 22 giugno 2012 Da 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.

 

Commenti Autore Data
very cool, now it all makes sense. Wilson Man 22 giugno 2012 13.10
May be the SQLQuery(Impl) could be extended so... Sampsa Sohlman 23 giugno 2012 15.24
NIce! 다니엘 Daniel Bastos 28 giugno 2012 7.42
FYI You are using _classMap in a non ... Jelmer Kuperus 9 luglio 2012 2.55
Nice Post... Jay Patel 10 luglio 2012 7.41
Hey jelmer, Good points. I will update my... David Truong 10 luglio 2012 12.59
Hey Sampsa, That would make things too easy... David Truong 10 luglio 2012 13.01
It is a bit challenging to add a new action... Muhammed Shakir 7 ottobre 2012 3.09

very cool, now it all makes sense.
Inviato il 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.
Inviato il 23/06/12 15.24.
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
Inviato il 09/07/12 2.55.
Inviato il 10/07/12 7.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
Inviato il 10/07/12 12.59 in risposta 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.
Inviato il 10/07/12 13.01 in risposta 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
Inviato il 07/10/12 3.09 in risposta a David Truong.