Vista combinada Visión Plana Vista de árbol
Discusiones [ Anterior | Siguiente ]
toggle
Joseph Hobbs
Recommended approach to storing custom settings for related porlets?
10 de julio de 2012 11:51
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

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
David H Nebinger
RE: Recommended approach to storing custom settings for related porlets?
10 de julio de 2012 12:08
Respuesta

David H Nebinger

Ranking: Liferay Legend

Mensajes: 7250

Fecha de incorporación: 1 de septiembre de 2006

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.
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
10 de julio de 2012 12:15
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

Mensajes recientes

Thanks for the insight. I'll give ServiceBuilder a look and see if I can figure things out. Thanks again!
Hitoshi Ozawa
RE: Recommended approach to storing custom settings for related porlets?
10 de julio de 2012 14:19
Respuesta

Hitoshi Ozawa

Ranking: Liferay Legend

Mensajes: 7954

Fecha de incorporación: 23 de marzo de 2010

Mensajes recientes

Since you want to scope by site, use companyId AND groupId
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
11 de julio de 2012 12:25
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

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?
Hitoshi Ozawa
RE: Recommended approach to storing custom settings for related porlets?
11 de julio de 2012 14:44
Respuesta

Hitoshi Ozawa

Ranking: Liferay Legend

Mensajes: 7954

Fecha de incorporación: 23 de marzo de 2010

Mensajes recientes

Following codes in jsp will retrieve current user's information:

<liferay-theme:defineObjects />
long companyId = themeDisplay.getCompanyId();
long groupId = themeDisplay.getScopeGroupId();
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
25 de julio de 2012 12:44
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

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)

 1
 2<?xml version="1.0" encoding="UTF-8"?>
 3<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.1.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_1_0.dtd">
 4<service-builder package-path="gui.dao">
 5    <author>Joseph Hobbs</author>
 6    <namespace>nwmgui</namespace>
 7    <entity name="NWMSetting" local-service="true" remote-service="false">
 8        <column name="settingId" type="long" primary="true"></column>
 9        <column name="groupId" type="long"></column>
10        <column name="companyId" type="long"></column>
11        <column name="settingName" type="String"></column>
12        <column name="settingValue" type="String"></column>
13        <order by="asc">
14            <order-column name="settingName" order-by="asc"></order-column>
15        </order>
16        <finder return-type="Collection" name="Site">
17            <finder-column name="companyId"></finder-column>
18            <finder-column name="groupId"></finder-column>
19        </finder>
20        <finder name="NameAndSite" return-type="NWMSetting">
21            <finder-column name="settingName"></finder-column>
22            <finder-column name="companyId"></finder-column>
23            <finder-column name="groupId"></finder-column>
24        </finder>
25    </entity>
26</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.

 1
 2public NWMSetting getNWMSettingByNameAndSite(
 3                            String settingName,
 4                            long groupId,
 5                            long companyId)
 6                            throws PortalException,
 7                                   SystemException
 8{
 9    return this.nwmSettingPersistence.fetchByNameAndSite(
10                            settingName,
11                            companyId,
12                            groupId);
13}
14
15public List<NWMSetting> getNWMSettingsBySite(
16                            long groupId,
17                            long companyId)
18                            throws SystemException
19{
20    return this.nwmSettingPersistence.findBySite(companyId, groupId);
21}


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.

 1
 2@Override
 3public NWMSetting addNWMSetting(NWMSetting nwmSetting)
 4                            throws SystemException
 5{
 6    /**
 7     * if the setting ID is -1, this is a new entry that has not been
 8     * assigned a new ID.  Generate a new settingId and pass the request
 9     * through to the parent class for processing.
10     */
11
12    if(nwmSetting.getSettingId() == -1)
13    {
14        long settingId = CounterLocalServiceUtil.increment(
15                               NWMSetting.class.getName());
16        nwmSetting.setSettingId(settingId);
17    }
18
19    return super.addNWMSetting(nwmSetting);
20}


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

1
2NWMSetting setting = NWMSettingLocalServiceUtil
3                         .getNWMSettingByNameAndSite(
4                                settingName,
5                                groupId,
6                                companyId);


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

 1
 2NWMSetting setting = NWMSettingLocalServiceUtil.createNWMSetting(-1);
 3
 4setting.setGroupId(this.groupId);
 5setting.setCompanyId(this.companyId);
 6setting.setSettingName(settingName);
 7setting.setSettingValue(settingValue);
 8
 9NWMSettingLocalServiceUtil.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.

 1
 2import com.liferay.portal.model.Group;
 3
 4/**
 5 * Represents the Liferay fields that designate a specific portal site.
 6 *
 7 * @author Joseph Hobbs
 8 */
 9public class PortalId
10{
11    /**
12     * Local storage for the Liferay Company ID.
13     */
14    private long companyId;
15
16    /**
17     * Local storage for the Liferay Group ID.
18     */
19    private long groupId;
20
21    /**
22     * Constructor allowing creation of object using the provided Group.
23     *
24     * @param group Group object to interrogate for information
25     */
26    public PortalId(Group group)
27    {
28        this.groupId = group.getGroupId();
29
30        this.companyId = 0;
31        if (group.isOrganization())
32        {
33            this.companyId = group.getOrganizationId();
34        }
35    }
36
37    /**
38     * Returns the Liferay Portal site's company ID.
39     *
40     * @return company ID
41     */
42    public long getCompanyID()
43    {
44        return this.companyId;
45    }
46
47    /**
48     * Returns the Liferay Portal site's group ID.
49     *
50     * @return group ID
51     */
52    public long getGroupID()
53    {
54        return this.groupId;
55    }
56}


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

1
2public static final PortalId resolvePortalId(PortletRequest request)
3{
4    ThemeDisplay themeDisplay =
5            (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
6
7    return new PortalId(themeDisplay.getScopeGroup());
8}


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

 1
 2@Override
 3public void doView(...)
 4{
 5    PortalId portalId = PortletUtils.resolvePortalId(renderRequest);
 6
 7    // do something
 8}
 9
10public void doAction(...)
11{
12    PortalId portalId = PortletUtils.resolvePortalId(actionRequest);
13
14    // do something
15}


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!
David H Nebinger
RE: Recommended approach to storing custom settings for related porlets?
24 de julio de 2012 7:56
Respuesta

David H Nebinger

Ranking: Liferay Legend

Mensajes: 7250

Fecha de incorporación: 1 de septiembre de 2006

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:

 1
 2NWMSetting setting = NWMSettingLocalServiceUtil.createNWMSetting(-1);
 3
 4setting.setGroupId(this.groupId);
 5setting.setCompanyId(this.companyId);
 6setting.setSettingName(settingName);
 7setting.setSettingValue(settingValue);
 8
 9NWMSettingLocalServiceUtil.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.
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
24 de julio de 2012 8:01
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

Mensajes recientes

Thanks for the feedback on that. I've corrected my post (and project) accordingly to reflect your recommendation!
David H Nebinger
RE: Recommended approach to storing custom settings for related porlets?
24 de julio de 2012 8:05
Respuesta

David H Nebinger

Ranking: Liferay Legend

Mensajes: 7250

Fecha de incorporación: 1 de septiembre de 2006

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...
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
24 de julio de 2012 8:16
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

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!
Hitoshi Ozawa
RE: Recommended approach to storing custom settings for related porlets?
24 de julio de 2012 16:17
Respuesta

Hitoshi Ozawa

Ranking: Liferay Legend

Mensajes: 7954

Fecha de incorporación: 23 de marzo de 2010

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>
Joseph Hobbs
RE: Recommended approach to storing custom settings for related porlets?
25 de julio de 2012 12:44
Respuesta

Joseph Hobbs

Ranking: New Member

Mensajes: 14

Fecha de incorporación: 10 de julio de 2012

Mensajes recientes

Interesting point on indexing. I've updated my code and the above post to account for your recommendation. Thanks!