Thursday, April 21, 2011

WebCenter domain configuration for WebCenter Spaces APIs

While using your WebCenter Portal application, users may encounter situations where a Space would be useful to help them complete a particular task. In such cases, it would be much less disruptive to remain within the context of the current application, rather than having to switch to the WebCenter Spaces application. To this end, WebCenter Spaces provides access to a subset of its Space functionality through several APIs. Using these APIs, you can integrate powerful Space functionality into your WebCenter Portal application.
You can use WebCenter Spaces APIs to:
  • Create and manage Spaces and Space templates. You can create and delete Spaces, and add custom attributes. For more information, see (WebCenter Developer's Guide) Section 50.2.5.1, "Managing Spaces and Space Template."Manage Space membership.
  • You can add and remove Space members. For more information, see (WebCenter Developer's Guide) Section 50.2.5.2, "Managing Space Membership."
  • Retrieve information about Spaces and Space templates. For example, you can retrieve the WebCenter Spaces URL or the URL of a specific Space. You can also retrieve Space and Space template metadata. For more information, see (WebCenter Developer's Guide) Section 50.2.5.3, "Retrieving Information for Spaces and Space Templates."

WebCenter Spaces APIs are contained within several classes. For more information on the different classes with descriptions on the purpose of the APIs within each class, see Table 50-1 in the WebCenter Developers Guide.  In this post I will explain how to set up your portal application, and how to prepare your environment to support using the WebCenter Spaces APIs.

The first step describes the administrator tasks required to configure WS-Security for WebCenter Spaces so that the communication between the an application exposing WebCenter Spaces APIs (the client) and WebCenter Spaces (the producer) is secure, and that the identity of the user invoking the APIs is protected. In this section I will give examples of only the actual commands. If you would like more information on each of the command arguments are, please see Section 32 in the WebCenter Administration Guide.

Creating the WebCenter (Producer) Domain Keystore

To create and manage the keys and certificates in the JKS, use the keytool utility that is distributed with the Java JDK 6. Using keytool, generate a key pair:

keytool -genkeypair -keyalg RSA -dname "cn=spaces,dc=example,dc=com" -alias webcenter -keypass welcome1 -keystore webcenter.jks -storepass welcome1 -validity 1064

Export the Certificate containing the public key

keytool -exportcert -v -alias webcenter -keystore webcenter.jks -storepass welcome1 -rfc -file webcenter_public.cer

Importing the Certificate

keytool -importcert -alias webcenter_public -file webcenter_public.cer -keystore webcenter.jks -storepass welcome1

Note: If you are deploying your application to the same domain, you do not have to create the client keystore and keys. However, if the client is in a different domain, for example, testing your application in JDeveloper embedded WebLogic Server, you will need to do the next steps.

Creating the JDeveloper (Client) Domain Keystore

keytool -genkeypair -keyalg RSA -dname "cn=external_webcenter_custom,dc=example,dc=com" -alias external_webcenter_custom -keypass welcome1 -keystore external_webcenter_custom.jks -storepass welcome1 -validity 1024

Export the Certificate containing the public key

keytool -exportcert -v -alias external_webcenter_custom -keystore external_webcenter_custom.jks -storepass welcome1 -rfc -file external_webcenter_custom_public_key.cer

Import the Certificates

keytool -importcert -alias external_webcenter_custom_public_key -file external_webcenter_custom_public_key.cer -keystore webcenter.jks -storepass welcome1

This step is IMPORTANT as it contains the certificate from the WebCenter Domain

keytool -importcert -alias webcenter_public -file webcenter_public.cer -keystore external_webcenter_custom.jks -storepass welcome1

Once that is done you should have created 2 keystores, (producer) webcenter.jks (alias=webcenter) and (client) external_webcenter_custom.jks (alias=external_webcenter_custom). Pay close attention to the client alias name. This name is important as it will be used as a parameter in Spaces API code.  The next steps are to register the keystores with both the WebCenter domain and JDeveloper (client) domain. This is a 2 step process. The first step is to modify the jps-config,xml and then use WLST to update the security credentials. Note before doing any of the proceeding modifications, be sure to BACK UP both the jps-config.xml and the cwallet.sso files.

Register Keystore in WebCenter (producer) Domain

Copy the keystore that you created for the WebCenter domain (webcenter.jks) to the fmwconfig directory. This directory is located in the [Oracle_Home]/user_projects/domains/[wc_domain]/config directory. In the same directory, open the jps-config.xml in a text editor. In this file, locate the "serviceInstance" node for the keystore.provider Provider: Update the "location" property for the new file name (i.e ./webcenter.jks).

serviceInstance name="keystore" provider="keystore.provider" location="./webcenter.jks" 

Update Security Credentials in WSLT

Invoke WLST from [webcenter_home]/common/bin. Execute the following commands, note that the user= references the alias name (webcenter):

updateCred(map="oracle.wsm.security", key="keystore-csf-key", user="owsm", password="welcome1", desc="Keystore key")


updateCred(map="oracle.wsm.security", key="enc-csf-key", user="webcenter", password="welcome1", desc="Encryption key")


updateCred(map="oracle.wsm.security", key="sign-csf-key", user="webcenter", password="welcome1", desc="Signing key")

Register Keystore in JDeveloper (client) Domain

Copy the keystore that you created for the JDeveloper domain (external_webcenter_custom.jks) to the fmwconfig directory. This directory is located in the [JDeveloper_sys_home]/DefaultDomain/config directory. For example, on my systems this location is:

C:\Users\[your-user]\AppData\Roaming\JDeveloper\system11.1.1.4.37.59.23\DefaultDomain\config\fmwconfig

In the same directory, open the jps-config.xml in a text editor. In this file, locate the "serviceInstance" node for the keystore.provider Provider: Update the "location" property for the new file name (i.e ./external_webcenter_custom.jks).

serviceInstance name="keystore" provider="keystore.provider" location="./external_webcenter_custom.jks"

Update Security Credentials in WSLT

Invoke WLST from [JDeveloper_Home]/oracle_common/common/bin. Execute the following commands, note that the user= references the alias name (external_webcenter_custom):

updateCred(map="oracle.wsm.security", key="keystore-csf-key", user="owsm", password="welcome1", desc="Keystore key")

updateCred(map="oracle.wsm.security", key="enc-csf-key", user="external_webcenter_custom", password="welcome1", desc="Encryption key")

updateCred(map="oracle.wsm.security", key="sign-csf-key", user="external_webcenter_custom", password="welcome1", desc="Signing key")

Commands for checking the keystores and credentials

Once you have completed the above steps there are commands that can enable you to check to see if everything is setup correctly (i.e. the alias names match to the correct domain).   For checking the credentials, run the following from WLST:

listCred(map="oracle.wsm.security", key="keystore-csf-key")
listCred(map="oracle.wsm.security", key="enc-csf-key")
listCred(map="oracle.wsm.security", key="sign-csf-key")

* Executing these commands against the WebCenter domain will give the following response:

[Name : owsm, Description : Keystore key, expiry Date : null]
PASSWORD:welcome1

[Name : webcenter, Description : Encryption key, expiry Date : null]PASSWORD:welcome1

[Name : webcenter, Description : Encryption key, expiry Date : null]PASSWORD:welcome1

* Executing these commands against the JDeveloper domain will give the following response:

[Name : owsm, Description : Keystore key, expiry Date : null]
PASSWORD:welcome1

[Name : external_webcenter_custom, Description : Encryption key, expiry Date : null]PASSWORD:welcome1

[Name : external_webcenter_custom, Description : Encryption key, expiry Date : null]PASSWORD:welcome1

For checking the keystores

keytool -list -v -keystore [keystore-name] -storepass [keystore-password]

Example of using the WebCenter Spaces API in JDeveloper

The following code snippet invokes a response from Spaces to return a list of group space names. Note the reference to the key alias from the keystore:

GroupSpaceWSContext context = new GroupSpaceWSContext(); context.setEndPoint("http://[host]:[port]/webcenter/SpacesWebService");

context.setSamlIssuerName(http://www.oracle.com/);
context.setRecipientKeyAlias("external_webcenter_custom");
try {

GroupSpaceWSClient groupSpaceWSClient = new GroupSpaceWSClient(context);
List gspaceList = groupSpaceWSClient.getGroupSpaces(null);
for(String name : gspaceList){
System.out.println(name);
}
} catch (Exception e) {...}

One final important note.  In order for the client code to work, you must also have a security token to provide the user.  This can be accomplished by enabling ADF security on the application, which is invoking the Spaces API code.

Friday, April 8, 2011

ADF Layouts Revisted

Designing UI pages in ADF can be somewhat cumbersome if you don't understand what really goes on behind the scenes when using the layout specific tags such as panelGroupLayout. OOTB JDeveloper comes with many different types of layout based tags. If you have read my previous post on layout management, you will notice that some of these tags are meant for specific layout design type patterns: Stretch (application LaF i.e. JDeveloper UI), or Flow (generic web site LaF). Moreover, using the layout tags in an "illegal" combination usually creates a design that renders the UI in a undesirable behavior. For example, stretchable items (i.e. tables) not stretching, or scrollable (flow) content not scrolling. To better understand how to create better layouts we must first understand what the layout components will actually turn into HTML wise on the browsers end. This knowledge will help not only in choosing the correct combination of layout tags, but will also enable you to use the power of CSS styles to "control" how the elements look and behave. The first layout tag that I am going to discuss is one of the most used tag, the panelGroupLayout.

* Tag name: af:panelGroupLayout

* UIComponent class:oracle.adf.view.rich.component.rich.layout.RichPanelGroupLayout

* Component type: oracle.adf.RichPanelGroupLayout

The panelGroupLayout control is a layout element that arranges its children in one of a few simple patterns. Each pair of adjacent children will be separated by an optional separator child. It can lay out children consecutively (wrapping as needed), or in a single horizontal line, or vertically.

Rules of thumb on usage:

* Never place a scrolling panelGroupLayout inside of another scrolling panelGroupLayout since users do not like nested scroll bars

* Never specify percentage heights in any component's inlineStyle attribute

Never use the "position" style in any component's inlineStyle attribute. Unless you are using fixed pixel heights on them, you would never place a panelSplitter or panelStretchLayout inside of any panelGroupLayout regardless of what you have set its layout attribute to.

If you believe that you need to break one of these rules, this is an indication that your page structure is not following the page structure guidelines and you will likely have troubles getting your application to render consistently across various web browsers and computing platforms. You may also alternatively place a facetRef, iterator, or switcher inside of the panelGroupLayout and their resolved children will be treated as if they were direct children of the panelGroupLayout. If you don't want this behavior, you may wrap the children you want to be grouped in a group component.

Geometry Management:

* This component CAN be stretched by a parent layout component that stretches its children, e.g., panelStretchLayout if: layout="scroll" or layout="vertical"

* This component CANNOT be stretched by a parent layout component that stretches its children if: layout="default" or layout="horizontal"

Ok, now that we understand what the adf side of things are. When you use the panelGroupLayout what happens on the browser side? Based on the layout "type" selected, this will mean different things. In general, here are the browser HTML equivalents to the different layout styles:

* layout= "default" will turn into a HTML “SPAN”

* layout = "vertical" will turn into a HTML “DIV”

* layout= “scroll” will also turn into a HTML “DIV” - with an added "style" setting to induce the vertical scrollbar to appear - overflow: auto; width: 100%; height: 100%;

* layout=”horizontal” will turn into a HTML “table”- with 1 HTML row “TD” defined. Each element added will become a HTML cell “TD”

To give an example of using the panelGroupLayout in combinations of other panelGroupLayouts that uses different layout settings, here is an image that shows a simple use case. Note: the colored borders are there to show each of the component elements.


The ADF components that were used are as follows:

This, in turn produces these HTML tags:


In this example, the top panelGroupLayout (bordered in green) has been set to layout=horizontal, which sets the tag in the browser to be an HTML “TABLE”. In this table body, you see 1 table row (TR) defined. Each subsequent item added to the row, becomes a table cell (TD). The 2 items that have been added are panelGroupLayout’s that have the layout set to “vertical”, which in turn creates a HTML “DIV”. Knowing this you can add standard CSS style elements to these ADF layout tags to create visual behavior such as padding and margin. This is just a example on how to use the appropriate ADF layout tags to create conventional visual designs. In addition, once you have mastered what the ADF to HTML tag equivalents are, you will be able to convert your HTML mockups into the ADF world. In my next post I will cover more of the layout tags.

Wednesday, March 9, 2011

Dynamic resizing for popup dialogs

In some specific circumstances you may need to fulfill a requirement that would have a popup dialog (containing for example, an adf table), resize itself dynamically based on the browser viewing size. This behavior is similar to how the panelCollection component handles the popup window that is invoked from the "detach" menu item. By default, the panelWindow, which is the immediate child of the popup, height (ContentHeight) and width (ContentWidth)property values are not set. This enables the popup to launch with the default size settings for the panelWindow:




















Of course you can always set the values on these 2 properties to alter the size on the panelWindow itself. Unfortunately, the trick will be to calculate the browser view dimensions in java, which in turn can be then passed/set on each of the content (width and height) property values. Now this issue can be solved by getting the browser view dimensions through javascript (there are plenty examples on the web for doing so), but then the next challenge would be to figure out a clever way on how to pass these (javascript based) values to the panelWindow component properties.

Fortunately, there is a much better way to get these dimensions through the ADF JavaScript API. In addition, I can also use javascript to pass the retreived dimension values to the panelWindow itself. The following javascript code fragment will do the trick:

function openPopup(popupId, panelWindowId)
{
return function(event)
{
var agent = AdfAgent.AGENT;
var windowWidth = agent.getWindowWidth();
var windowHeight = agent.getWindowHeight();
var popup = AdfPage.PAGE.findComponentByAbsoluteId(popupId);
var panelWindow = popup.findComponent(panelWindowId);
panelWindow.setContentWidth(Math.max(100, windowWidth-60));
panelWindow.setContentHeight(Math.max(100, windowHeight-80));


if(popup != null)
popup.show();
else
popup.hide();
}
}

Also note that this function also takes care of displaying the popup. Normally, you would invoke the popup through the af:showPopupBehavior (operations based tag). This tag is simply replaced by the af:clientListener tag. For example:

af:clientListener type="action" method="openPopup('popupId','panelWindowId')"

Using this methodology the working results are as such:



















So there you have it, a nice resizable popup without having to write alot of code. Thanks to my good friend and colleague Frank Nimphius on setting me on the right path in discovering this solution.

I have provided the code sample zip here

Friday, August 28, 2009

ADF Layout Overview and Best Practices

Creating custom applications in Oracle JDeveloper using the Oracle Webcenter framework can be quite productive using the included out-of-the box components, taskflows and services. However, when it come to designing the page UI, it can be a frustrating experience. This is due in part on how certain components interact with each other in terms of flow and/or the ability to stretch its contents. In the beginning, I learned how to "get along" with working with the layouts components through many trial and error experiences. Basically, there are some general guidelines and best practices that you need to follow so that your UI design exhibits its entended behavour:
  • Never try to stretch something vertically when inside of a flowing (non-stretched) container. If you attempting to do this, the result will exhibit inconsistent behavior across web browsers.
  • Never specify a height value with percent units. Instead, define such stretching declaratively as by following the process outlined below.
  • Never use the "position" style.
  • Create a stretchable outer frame. Build up the outer structure of your page using a structure of components that support being stretched and also stretch their children.
    Each layout or panel component's tag documentation will identify whether this is supported and how to achieve it in its "Geometry Management" documentation. Some components have attributes to determine whether children will be stretched or not. For example: "document" has its maximized attribute, "showDetailItem" has its stretchChildren attribute. Typically you would use various combinations of the decorativeBox, panelSplitter and panelStretchLayout components inside of your document component to create the stretchable outer frame.
  • Consider using pageTemplates, declarative components, and regions for reuse and centralized maintenance.
  • Create flowing islands. Inside of the stretchable structure created in step 1, create islands of flowing (non-stretched) components. To make this transition from stretching to flowing, use panelGroupLayout with layout="scroll" since it supports being stretched but will not stretch its children.
  • Do not attempt to stretch anything vertically inside of these flowing islands.
  • Many leaf components do not make sense by themselves. For example, if you have a series of input components, you would never want to just place these in a panelGroupLayout because it would be much better for usability if you placed them in a panelFormLayout so the labels and fields would line up.
  • A sample list of components that cannot be reliably stretched includes: Most input components, panelBorderLayout, panelFormLayout, panelGroupLayout (with layout="default") , panelGroupLayout (with layout="horizontal"), panelHeader (with type="flow"), panelLabelAndMessage, panelList, Apache MyFaces Trinidad HTML Component - tableLayout, and JSF HTML Component - panelGrid.
  • Use themed decorativeBox components to organize your page layers with visual distinction and decorative borders as seen in some of the sample skins. Note that not all skins have alternative themes so you may not see any distinction. If your decorativeBox components are not showing up with different colored backgrounds and you know that you are using a skin that has definitions for alternate themes, you might be missing a web.xml context-param setting for "oracle.adf.view.rich.tonalstyles.ENABLED" being set to false. Your document component also has a theme attribute so you can use it to change the main background styling of your page.
  • Use a custom skin for consistently modified appearances if the existing skin doesn't provide all that you need. For instance-specific alternative styling, use the styleClass attribute. Keep the corresponding style definitions in an easy-to-maintain location such as in a custom skin, in the metaContainer facet of the document component, or in a style provided by the resource tag.

As a last resort, use the component attributes such as inlineStyle, contentStyle, and labelStyle. These are less declarative, harder to maintain, contribute more to the page's raw HTML size, and may not even be needed if one or more of the above mechanisms are used.
Styles are directly processed by the web browser which gives you a great deal of power but at the cost of being less declarative and error-prone. The browsers do not support all styles on all elements and certain combinations of styles produce non-obvious results.

Here is some guidance on style configurations to avoid on all components:

  • An inlineStyle with a "height" value with "%" units
  • An inlineStyle with a "width" value between "90%" and "100%" (use styleClass="AFStretchWidth" or styleClass="AFAuxiliaryStretchWidth" instead)
  • An inlineStyle with "height", "top", and "bottom" values
  • An inlineStyle with "width", "left", and "right" values
  • An inlineStyle with a "position" value

Here is some guidance on style configurations to avoid on a Child component being stretched by a Parent component :

  • An inlineStyle with "width" or "height" values

Scrolling:

  • You should only have scrollbars around flowing island content. The recommended transition component for switching from a stretching outer frame into a flowing island is the panelGroupLayout with layout="scroll". If the contents of this panelGroupLayout cannot fit in the space allocated, the browser will determine whether scrollbars are needed and will add them automatically.
  • It is not recommended that you nest scrolling panelGroupLayout components because this will make the user see multiple scrollbars. Also, this should only be used at transitions from stretching to flowing areas and since you should not have stretching areas inside of flowing areas, you would generally never end up with nested scrollbars. It is best to minimize the number of areas that a user must scroll in order to see what he or she is looking for. Take time to consider what scrolling the user will need. In cases where undesired scrollbars exist, you may want to simply change the layout attribute of that panelGroupLayout to "vertical".
  • There is a known scrolling issue that has been filed against Internet Explorer 7.0.5730.11. The issue is only resolved in Internet Explorer 8 when running in pure IE8 rendering mode. If a scrolling box has contents that are set to be as wide as the containing box and if the contents are large enough to warrant the need for a vertical scrollbar, an unnecessarily-needed horizontal scrollbar will be added. The browser is failing to adjust the width of the contents for the presence of the vertical scrollbar and thus a horizontal scrollbar appears. This horizontal scrollbar lets you scroll the small amount of space equal to the width of the vertical scrollbar. With this issue, it is not recommended to specify a width anywhere between 90% and 100%. Smaller widths will generally not encounter the bug. Workarounds (as seen in this page) involve setting the widths of the contents to be smaller than full width so that the browser has enough space for a vertical scrollbar to fit. For your convenience, a styleClass named "AFStretchWidth" is built into the skin to specify that a component with this styleClass will get a reduced width in Internet Explorer 7 or full width in other browsers. If you need a smaller size for an thin auxiliary column, you can alternatively use "AFAuxiliaryStretchWidth" or you may create a similar skin definition in your own skin like this:

@agent ie and (version: 7.0) {
.AFIEOverflowWorkaround75 { width: 75%; }
}

Margins, borders, padding:

  • Thanks to the browser's "CSS Box Model rules", it is much harder to define margins, borders, and padding on your components than what you might expect. In many cases, to apply these kinds of styles, you need to use multiple components together. In a scrolling area, adding an extra panelGroupLayout with layout="vertical" with the padding defined on it inside of the outer layout="scroll" panelGroupLayout will be required. In a stretching area, you may need to wrap a component inside of a panelStretchLayout with spacers in its top, start, end, and bottom facets for the padding.

Oracle WebCenter Services - New Wikis, Blogs, and Discussions Portlet Samples

The new Wikis, Blogs, and Discussions Portlet Samples, which I co-developed with Skip Sauls, is now available on the webcenter.oracle.com site. The sample download section contain links for download both the deployable (to Weblogic Servcr) EAR file, and/or the entire JDeveloper application workspace. There are ReadMe files contained in each package. The information included are the main setup and configuration for the portlets to be consumed in Oracle Portal or in Webcenter custom application.

Please note that the portlets are "samples", which are entended for test, evaluation, and education purposes only. They are not supported through formal Oracle Support mechanisms. However, we are very interested in receiving your feedback on these portlets. Please post any suggestions, questions, or issues to the Oracle WebCenter Suite - Services discussion forum and we will respond as quickly as possible.