here are probably many on going debates whether to use portlets or taskflows in a WebCenter custom portal application. Usually the main battle on which side to take in these debates are centered around which technology enables better performance. The good news is that both of my colleagues, Maiko Rocha and George Maggessy have posted their respective views on this topic so I will not have to further the discussion. However, if you do plan to use portlets in a WebCenter custom portal application, this post will help you not have the "portlet skin mismatch" issue. An example of the presence of the mismatch can be view from the applications log:
The skin customsharedskin.desktop specified on the requestMap will be used even though the consumer's skin's styleSheetDocumentId on the requestMap does not match the local skin's styleSheetDocument's id. This will impact performance since the consumer and producer stylesheets cannot be shared. The producer styleclasses will not be compressed to avoid conflicts. A reason the ids do not match may be the jars are not identical on the producer and the consumer. For example, one might have trinidad-skins.xml's skin-additions in a jar file on the class path that the other does not have.
Notice that due to the mismatch the portlet's CSS will not be able to be compressed, which will most like impact performance in the portlet's consuming portal. The first part of the blog will define the portlet mismatch and cover some debugging tips that can help you solve the portlet mismatch issue. Following that I will give a complete example of the creating, using and sharing a shared skin in both a portlet producer and the consumer application.
Portlet Mismatch Defined
In general, when you consume/render an ADF page (or task flow) using the ADF Portlet bridge, the portlet (producer) would try to use the skin of the consumer page - this is called skin-sharing. When the producer cannot match the consumer skin, the portlet would generate its own stylesheet and reference it from its markup - this is called mismatched-skin. This can happen because:
The consumer and producer use different versions of ADF Faces, or
The consumer has additional skin-additions that the producer doesn't have or vice-versa, or
The producer does not have the consumer skin
For case (1) & (2) above, the producer still uses the consumer skin ID to render its markup. For case (3), the producer would default to using portlet skin.
If there is a skin mis-match then there may be a performance hit because:
The browser needs to fetch this extra stylesheet (though it should be cached unless expires caching is turned off)
The generated portlet markup uses uncompressed styles resulting in a larger markup
It is often not obvious when a skin mismatch occurs, unless you look for either of these indicators:
The log messages in the producer log, for example:
The skin blafplus-rich.desktop specified on the requestMap will not be used because the styleSheetDocument id on the requestMap does not match the local skin's styleSheetDocument's id. It could mean the jars are not identical. For example, one might have trinidad-skins.xml's skin-additions in a jar file on the class path that the other does not have.
View the portlet markup inside the iframe, there should be a <link> tag to the portlet stylesheet resource like this (note the CSS is proxied through consumer's resourceproxy):
<link rel=\"stylesheet\" charset=\"UTF-8\" type=\"text/css\" href=\"http:.../resourceproxy/portletId...252525252Fadf%252525252Fstyles%252525252Fcache%252525252Fblafplus-rich-portlet-d1062g-en-ltr-gecko.css... Using HTTP monitoring tool (eg, firebug, httpwatch), you can see a request is made to the portlet stylesheet resource (see URL above)
There are a number of reasons for mismatched-skin. For skin to match the producer and consumer must match the following configurations:
The ADF Faces version (different versions may have different style selectors)
Style Compression, this is defined in the web.xml (default value is false, i.e. compression is ON)
Tonal styles or themes, also defined in the web.xml via context-params
The same skin additions (jars with skin) are available for both producer and consumer. Skin additions are defined in the trinidad-skins.xml, using the <skin-addition> tags. These are then aggregated from all the jar files in the classpath. If there's any jar that exists on the producer but not the consumer, or vice veras, you get a mismatch.
Debugging Tips
Ensure the style compression and tonal styles/themes match on the consumer and producer, by looking at the web.xml documents for the consumer & producer applications
It is bit more involved to determine if the jars match. However, you can enable the Trinidad logging to show which skin-addition it is processing. To enable this feature, update the logging.xml log level of both the producer and consumer WLS to FINEST. For example, in the case of the WebLogic server used by JDeveloper:
$JDEV_USER_DIR/system<version number>/DefaultDomain/config/fmwconfig/servers/DefaultServer/logging.xml
Add a new entry:
<logger name="org.apache.myfaces.trinidadinternal.skin.SkinUtils" level="FINEST"/>
Restart WebLogic. Run the consumer page, you should see the following logging in both the consumer and producer log files. Any entries that don't match is the cause of the mismatch. The following is an example of what the log will produce with this setting:
[SRC_CLASS: org.apache.myfaces.trinidadinternal.skin.SkinUtils] [APP: WebCenter] [SRC_METHOD: _getMetaInfSkinsNodeList]
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/announcement-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/calendar-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/custComps-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/forum-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/page-service-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/peopleconnections-kudos-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/peopleconnections-wall-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/portlet-client-adf-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/rtc-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/serviceframework-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/smarttag-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.skin/in1ar8/APP-INF/lib/spaces-service-skins.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/oracle.webcenter.composer/3yo7j/WEB-INF/lib/custComps-skin.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/adf.oracle.domain.webapp/q433f9/WEB-INF/lib/adf-richclient-impl-11.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/adf.oracle.domain.webapp/q433f9/WEB-INF/lib/dvt-faces.jar!/META-INF/trinidad-skins.xml
Processing skin URL:zip:/tmp/_WL_user/adf.oracle.domain.webapp/q433f9/WEB-INF/lib/dvt-trinidad.jar!/META-INF/trinidad-skins.xml
The Complete Example
The first step is to create the shared library. The WebCenter documentation covering this is located here in section 15.7. In addition, our ADF guru Frank Nimphius also covers this in hes blog. Here are my steps (in JDeveloper) to create the skin that will be used as the shared library for both the portlet producer and consumer.
Create a new Generic Application
Give application name (i.e. MySharedSkin)
Give a project name (i.e. MySkinProject)
Leave Project Technologies blank (none selected), and click Finish
Create the trinidad-skins.xml
Right-click on the MySkinProject node in the Application Navigator and select "New"
In the New Galley, click on "General", select "File" from the Items, and click OK
In the Create File dialog, name the file trinidad-skins.xml, and (IMPORTANT) give the directory path to MySkinProject\src\META-INF
In the trinidad-skins.xml, complete the skin entry. for example:
<?xml version="1.0" encoding="windows-1252" ?> <skins xmlns="http://myfaces.apache.org/trinidad/skin"> <skin> <id>mysharedskin.desktop</id> <family>mysharedskin</family> <extends>fusionFx-v1.desktop</extends> <style-sheet-name>css/mysharedskin.css</style-sheet-name> </skin> </skins>
Create CSS file
In the Application Navigator, right click on the META-INF folder (where the trinidad-skins.xml is located), and select "New"
In the New
Gallery, select Web-Tier-> HTML, CSS File from the the Items and click OK
In the Create Cascading Style Sheet dialog, give the name (i.e. mysharedskin.css)
Ensure that the Directory path is the under the META-INF (i.e. MySkinProject\src\META-INF\css)
Once the new CSS opens in the editor, add in a style selector. For example, this selector will style the background of a particular panelGroupLayout:
af|panelGroupLayout.customPGL{ background-color:Fuchsia; }
Create the MANIFEST.MF (used for deployment JAR)
In the Application Navigator, right click on the META-INF folder (where the trinidad-skins.xml is located), and select "New"
In the New Galley, click on "General", select "File" from the Items, and click OK
In the Create File dialog, name the file MANIFEST.MF, and (IMPORTANT) ensure that the directory path is to MySkinProject\src\META-INF
Complete the MANIFEST.MF, where the extension name is the shared library name
Manifest-Version: 1.1 Created-By: Martin Deh Implementation-Title: mysharedskin Extension-Name: mysharedskin.lib.def Specification-Version: 1.0.1 Implementation-Version: 1.0.1 Implementation-Vendor: MartinDeh
Create new Deployment Profile
Right click on the MySkinProject node, and select New
From the New
Gallery, select General->Deployment Profiles, Shared Library JAR File from Items, and click OK
In the Create Deployment Profile dialog, give name (i.e.mysharedskinlib) and click OK
In the Edit JAR Deployment dialog, un-check Include Manifest File option
Select Project Output->Contributors, and check Project Source Path
Select Project Output->Filters, ensure that all items under the META-INF folder are selected
Click OK to exit the Project Properties dialog
Deploy the shared lib to WebLogic (start server before steps)
Right click on MySkin Project and select Deploy
For this example, I will deploy to JDeverloper WLS
In the Deploy dialog, select Deploy to Weblogic Application Server and click Next
Choose IntegratedWebLogicServer and click Next
Select Deploy to selected instances in the domain radio, select Default Server (note: server must be already started), and ensure Deploy as a shared Library radio is selected
Click Finish
Open the WebLogic console to see the deployed shared library
The following are the steps to create a simple test Portlet
Create a new WebCenter Portal - Portlet Producer Application
In the Create Portlet Producer dialog, select default settings and click Finish
Right click on the Portlets node and select New
IIn the New
Gallery, select Web-Tier->Portlets, Standards-based Java Portlet (JSR 286) and click OK
In the General Portlet information dialog, give portlet name (i.e. MyPortlet) and click Next 2 times, stopping at Step 3
In the Content Types, select the "view" node, in the Implementation Method, select the Generate ADF-Faces JSPX radio and click Finish
Once the portlet code is generated, open the view.jspx in the source editor
Based on the simple CSS entry, which sets the background color of a panelGroupLayout, replace the <af:form/> tag with the example code
<af:form> <af:panelGroupLayout id="pgl1" styleClass="customPGL"> <af:outputText value="background from shared lib skin" id="ot1"/> </af:panelGroupLayout> </af:form>
Since this portlet is to use the shared library skin, in the generated trinidad-config.xml, remove both the skin-family tag and the skin-version tag
In the Application Resources view, under Descriptors->META-INF, double-click to open the weblogic-application.xml
Add a library reference to the shared skin library (note: the library-name must match the extension-name declared in the MANIFEST.MF):
<library-ref> <library-name>mysharedskin.lib.def</library-name> </library-ref>
Notice that a reference to oracle.webcenter.skin exists. This is important if this portlet is going to be consumed by a WebCenter Portal application. If this tag is not present, the portlet skin mismatch will happen.
Configure the portlet for deployment
Create Portlet deployment WAR
Right click on the Portlets node and select New
In the New
Gallery, select Deployment Profiles, WAR file from Items and click OK
In the Create Deployment Profile dialog, give name (i.e. myportletwar), click OK
Keep all of the defaults, however, remember the Context Root entry (i.e. MyPortlet4SharedLib-Portlets-context-root, this will be needed to obtain the producer WSDL URL)
Click OK, then OK again to exit from the Properties dialog
Since the weblogic-application.xml has to be included in the deployment, the portlet must be deployed as a WAR, within an EAR
In the Application dropdown, select Deploy->New Deployment Profile...
By default EAR File has been selected, click OK
Give Deployment Profile (EAR) a name (i.e. MyPortletProducer) and click OK
In the Properties dialog, select Application Assembly and ensure that the myportletwar is checked
Keep all of the other defaults and click OK
For this demo, un-check the Auto Generate ..., and all of the Security Deployment Options, click OK
Save All
In the Application dropdown, select Deploy->MyPortletProducer
In the Deployment Action, select Deploy to Application Server, click Next
Choose IntegratedWebLogicServer and click Next
Select Deploy to selected instances in the domain radio, select Default Server (note: server must be already started), and ensure Deploy as a standalone Application radio is selected
The select deployment type (identifying the deployment as a JSR 286 portlet) dialog appears. Keep default radio "Yes" selection and click OK
Open the WebLogic console to see the deployed Portlet
The last step is to create the test portlet consuming application. This will be done using the OOTB WebCenter Portal - Framework Application.
Create the Portlet Producer Connection
In the JDeveloper Deployment log, copy the URL of the portlet deployment (i.e. http://localhost:7101/MyPortlet4SharedLib-Portlets-context-root
Open a browser and paste in the URL. The Portlet information page should appear. Click on the WSRP v2 WSDL link
Copy the URL from the browser (i.e. http://localhost:7101/MyPortlet4SharedLib-Portlets-context-root/portlets/wsrp2?WSDL)
In the Application Resources view, right click on the Connections folder and select New Connection->WSRP Connection
Give the producer a name or accept the default, click Next
Enter (paste in) the WSDL URL, click Next
If connection to Portlet is succesful, Step 3 (Specify Additional ...) should appear. Accept defaults and click Finish
Add the portlet to a test page
Open the home.jspx. Note in the visual editor, the orange dashed border, which identifies the panelCustomizable tag.
From the Application Resources. select the MyPortlet portlet node, and drag and drop the node into the panelCustomizable section. A Confirm Portlet Type dialog appears, keep default ADF Rich Portlet and click OK
Configure the portlet to use the shared skin library
Open the weblogic-application.xml and add the library-ref entry (mysharedskin.lib.def) for the shared skin library. See create portlet example above for the steps
Since by default, the custom portal using a managed bean to (dynamically) determine the skin family, the default trinidad-config.xml will need to be altered
Open the trinidad-config.xml in the editor and replace the EL (preferenceBean) for the skin-family tag, with mysharedskin (this is the skin-family named defined in the trinidad-skins.xml)
Remove the skin-version tag
Right click on the index.html to test the application
Notice that the JDeveloper log view does not have any reporting of a skin mismatch. In addition, since I have configured the extra logging outlined in debugging section above, I can see the processed skin jar in both the producer and consumer logs:
<SkinUtils> <_getMetaInfSkinsNodeList> Processing skin URL:zip:/JDeveloper/system11.1.1.6.38.61.92/DefaultDomain/servers/DefaultServer/upload/mysharedskin.lib.def/
[email protected]/app/mysharedskinlib.jar!/META-INF/trinidad-skins.xml