Foros de discusión

Recommended approach to storing custom settings for related porlets?

thumbnail
Joseph Hobbs, modificado hace 11 años.

Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
I am in the process of building a custom application and would like to leverage Liferay. I anticipate this application will be a single deployment consisting of multiple portlets providing various utilities for managing the application and associated data. This application will interface with an external system under the covers, so I have a small collection of settings I need to make available to these portlets. Is there a recommended approach for storing these 'settings'?

In reviewing the forums/documentation, it looks like portlet preferences are an option for storing settings associated with a single portlet. Given I'd like to store a collection of settings to be used by ALL portlets, this doesn't quite fit the goal. I also see references for storing settings in a custom properties file the portlets could load, but I would really like to make the settings configurable through a control panel portlet versus requiring the portal administrator prepare these text files in advance.

One option I thought about was utilizing PropUtils and setting my own custom properties (which I anticipate would be persisted within the Liferay database). I figure as long as I don't collide with an existing parameter, I would not anticipate any major issues in this case. Is it inappropriate for me to do this?

My only concern with using PropUtils (aside from appropriateness) is scoping. If I wanted the application to be associated with a SITE versus all of Liferay, how would I scope these settings to only apply to the specific site in question? Would I have to do that work myself (i.e. naming my keys appropriately), or is there some way to tell Liferay that this setting only applies to a specific site?

Am I on the right track on this, or is there something I've not yet stumbled upon? Any feedback is greatly appreciated. Thanks!

Joe
thumbnail
David H Nebinger, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets? (Respuesta)

Liferay Legend Mensajes: 14916 Fecha de incorporación: 2/09/06 Mensajes recientes
Use ServiceBuilder to create your own entity w/ associated table. Follow the SB standards for including the company ID as part of the entity to scope correctly. All portlets (whether deployed separately or in one war file artifact) can invoke the XxxLocalServiceUtil class to get and/or set the values. No properties files to contend with, no other real issues at all.
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
Thanks for the insight. I'll give ServiceBuilder a look and see if I can figure things out. Thanks again!
thumbnail
Hitoshi Ozawa, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

Liferay Legend Mensajes: 7942 Fecha de incorporación: 24/03/10 Mensajes recientes
Since you want to scope by site, use companyId AND groupId
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
I've been poking away at this, and I think I'm missing something... I've included companyId and groupId as fields in my definition as recommended. From there it looks like I need to populate these fields while saving or key off them while searching, which makes sense. But where do I get these values at runtime in my portlet? Do I always defer to the User.getCompanyId and User.getGroupId methods? Do these values change as users traverse between sites in the same portlet?
thumbnail
Hitoshi Ozawa, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

Liferay Legend Mensajes: 7942 Fecha de incorporación: 24/03/10 Mensajes recientes
Following codes in jsp will retrieve current user's information:

<liferay-theme:defineObjects />
long companyId = themeDisplay.getCompanyId();
long groupId = themeDisplay.getScopeGroupId();
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets? (Respuesta)

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
I just wanted to circle back around and say thanks for the pointers. I did end up getting this implemented using ServiceBuilder and it's working very well. I ended up creating a service that allowed me to store key/value pairs that I could easily reference on demand. I then wrapped this with a 'configuration object' that allowed me to set/get the parameters in a 'friendly' manner.

The biggest challenge I ran into was around the groupId/companyId stuff. I see it documented in many places that I should include these so my portlet can leverage scoping based on which Liferay site is being viewed. The hardest part was figuring out the right way to get at the data so I could pull my configuration options in the right context. I'm guessing doing too many 'new' things at one time (Liferay, Portlets, etc) attributed to that. lol

Long story short, here's a quick breakdown of what I did for those who might be struggling with some of the same concepts/challenge I was. In hindsight it's all pretty simple, but it didn't look quite this easy from the other end.

First, my ServiceBuilder configuration (service.xml)


<!--?xml version="1.0" encoding="UTF-8"?-->

<service-builder package-path="gui.dao">
	<author>Joseph Hobbs</author>
	<namespace>nwmgui</namespace>
    <entity name="NWMSetting" local-service="true" remote-service="false">
        <column name="settingId" type="long" primary="true"></column>
        <column name="groupId" type="long"></column>
        <column name="companyId" type="long"></column>
        <column name="settingName" type="String"></column>
        <column name="settingValue" type="String"></column>
        <order by="asc">
            <order-column name="settingName" order-by="asc"></order-column>
        </order>
        <finder return-type="Collection" name="Site">
            <finder-column name="companyId"></finder-column>
            <finder-column name="groupId"></finder-column>
        </finder>
        <finder name="NameAndSite" return-type="NWMSetting">
            <finder-column name="settingName"></finder-column>
            <finder-column name="companyId"></finder-column>
            <finder-column name="groupId"></finder-column>
        </finder>
    </entity>
</service-builder>


In the above, I included an ID, Name, and Value field for my data. In addition, companyId and groupId were included to allow for different settings per Site in the same Liferay Portal. I also included two finders; one to get all items for a specific Site and another for a specific setting within a specific Site.

Once my services were generated, I updated my NWMSettingLocalServiceImpl and added some methods to allow for finding and storing values. getNWMSettingByNameAndSite() and getNWMSettingsBySite() allow me to retrieve the values by scope.


public NWMSetting getNWMSettingByNameAndSite(
                            String settingName,
                            long groupId,
                            long companyId)
                            throws PortalException,
                                   SystemException
{
    return this.nwmSettingPersistence.fetchByNameAndSite(
                            settingName,
                            companyId,
                            groupId);
}

public List<nwmsetting> getNWMSettingsBySite(
                            long groupId,
                            long companyId)
                            throws SystemException
{
    return this.nwmSettingPersistence.findBySite(companyId, groupId);
}
</nwmsetting>


In addition, I overrode addNWMSetting() so that I could inject the settingId during the add process if it wasn't already populated. I didn't want to increment the counter for this field unless I was actually performing a save operation, nor did I want to deal with it every time I generated a new NWMSetting object. Instead I set any 'new' entries to an ID of '-1', and capture that during the add operation. I make my minor update and have ServiceBuilder perform the add as it would normally.


@Override
public NWMSetting addNWMSetting(NWMSetting nwmSetting)
                            throws SystemException
{
    /**
     * if the setting ID is -1, this is a new entry that has not been
     * assigned a new ID.  Generate a new settingId and pass the request
     * through to the parent class for processing.
     */

    if(nwmSetting.getSettingId() == -1)
    {
        long settingId = CounterLocalServiceUtil.increment(
                               NWMSetting.class.getName());
        nwmSetting.setSettingId(settingId);
    }

    return super.addNWMSetting(nwmSetting);
}


Once all that is done, I can easily grab a setting like so...


NWMSetting setting = NWMSettingLocalServiceUtil
                         .getNWMSettingByNameAndSite(
                                settingName,
                                groupId,
                                companyId);


Or create a new one and store it like so...


NWMSetting setting = NWMSettingLocalServiceUtil.createNWMSetting(-1);

setting.setGroupId(this.groupId);
setting.setCompanyId(this.companyId);
setting.setSettingName(settingName);
setting.setSettingValue(settingValue);

NWMSettingLocalServiceUtil.updateNWMSetting(setting);


The only other real honorable mention here was getting at the groupId/companyId data in the portlet. My biggest challenge here I think was my lack of experience with portlet's overall.

To facilitate the whole Liferay Site scoping, I created a small helper object called PortalId. this allowed me to simplify the resolution of the scoping data and put it in something simple to pass around as needed.


import com.liferay.portal.model.Group;

/**
 * Represents the Liferay fields that designate a specific portal site.
 *
 * @author Joseph Hobbs
 */
public class PortalId
{
    /**
     * Local storage for the Liferay Company ID.
     */
    private long companyId;

    /**
     * Local storage for the Liferay Group ID.
     */
    private long groupId;

    /**
     * Constructor allowing creation of object using the provided Group.
     *
     * @param group Group object to interrogate for information
     */
    public PortalId(Group group)
    {
        this.groupId = group.getGroupId();

        this.companyId = 0;
        if (group.isOrganization())
        {
            this.companyId = group.getOrganizationId();
        }
    }

    /**
     * Returns the Liferay Portal site's company ID.
     *
     * @return company ID
     */
    public long getCompanyID()
    {
        return this.companyId;
    }

    /**
     * Returns the Liferay Portal site's group ID.
     *
     * @return group ID
     */
    public long getGroupID()
    {
        return this.groupId;
    }
}


I then use a simple utility method to query the ActionRequest/RenderRequest object to pull the associated information.


public static final PortalId resolvePortalId(PortletRequest request)
{
    ThemeDisplay themeDisplay =
            (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);

    return new PortalId(themeDisplay.getScopeGroup());
}


At that point, I can easily get at the data on demand within my portlet.


@Override
public void doView(...)
{
    PortalId portalId = PortletUtils.resolvePortalId(renderRequest);

    // do something
}

public void doAction(...)
{
    PortalId portalId = PortletUtils.resolvePortalId(actionRequest);

    // do something
}


Hopefully this helps someone else work through the initial challenges as well. If anyone see's something I really foobar'd or should avoid, feel free to toss that out as well!
thumbnail
David H Nebinger, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

Liferay Legend Mensajes: 14916 Fecha de incorporación: 2/09/06 Mensajes recientes
One small modification...

When using the NWMSettingLocalServiceUtil.addNWMSetting(setting) or NWMSettingLocalServiceUtil.updateNWMSetting(setting) methods, you should not be instantiating a new instance yourself.

Instead you should be doing something like:


NWMSetting setting = NWMSettingLocalServiceUtil.createNWMSetting(-1);

setting.setGroupId(this.groupId);
setting.setCompanyId(this.companyId);
setting.setSettingName(settingName);
setting.setSettingValue(settingValue);

NWMSettingLocalServiceUtil.updateNWMSetting(setting);


The reason you don't instantiate yourself is because of the class loader. You want the plugin providing the service to create the instance in it's class loader, rather than whatever service is consuming the service using the NWMSettingLocalServiceUtil class.
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
Thanks for the feedback on that. I've corrected my post (and project) accordingly to reflect your recommendation!
thumbnail
David H Nebinger, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

Liferay Legend Mensajes: 14916 Fecha de incorporación: 2/09/06 Mensajes recientes
You missed the removal of the "setting.setSettingId(-1)" from your post. When you call createNWMSetting(primary key), it will set the primary key for you. You don't want to change it on your own...
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
Doh. I noticed/fixed that during my source code update, but didn't think to carry it over here for some reason. Thanks for the catch!
thumbnail
Hitoshi Ozawa, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

Liferay Legend Mensajes: 7942 Fecha de incorporación: 24/03/10 Mensajes recientes
Sorry for being late, one pointer: Change the order of your column name so companyId and groupId comes directly after settingId. In some database, order of index effects performance. I think you'll be searching for settingName/settingValue within a liferay instance within a groupId.

<column name="settingId" type="long" primary="true"></column>
<column name="companyId" type="long"></column>
<column name="groupId" type="long"></column>
<column name="settingName" type="String"></column>
<column name="settingValue" type="String"></column>
thumbnail
Joseph Hobbs, modificado hace 11 años.

RE: Recommended approach to storing custom settings for related porlets?

New Member Mensajes: 14 Fecha de incorporación: 10/07/12 Mensajes recientes
Interesting point on indexing. I've updated my code and the above post to account for your recommendation. Thanks!