72 WEM Framework: Developing Applications

The "Articles" sample application is used throughout this chapter to illustrate the basic architecture of an application that makes REST calls.

This chapter contains the following sections:

72.1 Application Structure

Figure 72-1 shows the source structure of the "Articles" sample application. On deployment, the following directories are copied from source to target: The contents of the lib directory are copied to /WEB-INF/lib/. The contents of the resources directory are copied to /WEB-INF/classes/.

Figure 72-1 'Articles' Sample Application Source Structure

Description of Figure 72-1 follows
Description of "Figure 72-1 'Articles' Sample Application Source Structure"

"Articles" is a Java Web application developed on Spring MVC. The following pages are available:

  • /install.app is the "Articles" installation page, which also displays a confirmation message when the application is successfully installed

  • /home.app is the home page of the "Articles" application (Figure 72-3).

72.2 Configuration Files

  • applicationContext.xml (in /WEB-INF/) holds SSO and application-specific configurations (such as a predefined user and the site on which to enable the data model and assets).

  • spring-servlet.xml (in /WEB-INF/) is the default Spring configuration file. This file stores the Spring configuration and references the following controllers (described in the "Source Files" section):

    • HomeController

    • InstallController

    • LayoutController

    • ProxyController

    • log4j.properties (in /resources/) is the logging configuration file. On application deployment, it is copied from /resources/ to /WEB-INF/classes/.

Source Files

/sample app/articles/src/main/java/

The /sample/ folder contains the source files listed below:

  • Configuration.java is populated (by the Spring framework) from the applicationContext.xml file (described in Section 72.2, "Configuration Files").

  • HomeController.java is the home page controller, which renders a single home page. This controller reads the list of sample articles from the WebCenter Sites platform using the REST API and displays them on the home page.

    The sample articles consist of images and text, stored in /sample app/articles/src/main/resources/install. The sample articles are installed in the WebCenter Sites database by InstallController.java.

  • InstallController.java registers the "Articles" application, and writes the application's asset model and sample assets to the database

  • LayoutController.java displays the application's layout page (layout.app) used by the WEM UI framework. LayoutController.java is also used during the application registration procedure.

  • ProxyController.java delegates AJAX requests to the WebCenter Sites REST servlet.

  • TldUtil.java utility class contains TLD function implementations.

Installer Resources

/sample app/articles/src/main/resources/install

The /install/ folder contains the following resources, used by the InstallController to construct the home page (Figure 72-3):

  • strategies.png

  • strategies.txt

  • tips.png

  • tips.txt

Home Page Files

/sample app/articles/src/main/webapp/images

The /images/ folder contains:

  • articles.png icon (Figure 72-2), which represents the 'Articles" application in the applications bar

  • In Figure 72-3:

    • edit.png is the icon for the Edit function

    • save.png is the icon for the Save function

    • cancel.png is the icon for the Cancel function

Scripts

/sample app/articles/src/main/webapp/scripts

The /scripts/ folder contains the json2.js utility script, used to convert strings to and from JSON objects.

Styles

/sample app/articles/src/main/webapp/styles

The /styles/ folder contains main.css, which specifies CSS styles used by this Web application.

Views

/sample app/articles/src/main/WEB-INF/jsp

The /jsp/ folder contains:

  • home.jsp, which is used to render the home page view of the "Articles" application (Figure 72-3)

  • layout.jsp, which defines the application layout

WEB-INF

/sample app/articles/src/main/WEB-INF

The /WEB-INF/ folder contains:

72.3 Making REST Calls

WebCenter Sites REST resources support two types of input and output formats: XML and JSON. To get the desired return formats, you will need to set HTTP headers that specify the MIME type application/xml or application/json.

For example, when specifying input format to be XML, set Content-Type to application/xml. When specifying the output format, set Accept (the expected format) to application/xml. If other output formats are specified, they will be ignored. The default is XML, if not specified in Content-Type or Accept (for sample code, see lines 64 and 66 in Section 72.3.1, "Making REST Calls from JavaScript").

For more detailed information about REST calls, see the following topics in this section:

72.3.1 Making REST Calls from JavaScript

The following code (in home.jsp) performs AJAX calls to the asset REST services to save asset data. Note that the request is actually performed to the proxy controller which redirects the request to the destination REST service.

Note:

We use the JSON stringify library (http://json.org/js.html) to serialize a JavaScript object as a string. It is much more convenient to write JSON objects instead of strings.

  1. // Form the URL pointing to the asset service

  2. // to the proxy controller, which will redirect this request to the CS REST servlet.

  3. var idarr = assetId.split(":");

  4. var assetUrl = "${pageContext.request.contextPath}/REST/sites/${config.csSiteName}/types/" + idarr[0] + "/assets/" + idarr[1];

  5. // For the data object to be posted.

  6. var data =

  7. {

  8. "attribute" :

  9. [

  10. {

  11. "name" : "source",

  12. "data" :

  13. {

  14. "stringValue" : document.getElementById("source_e_" + assetId).value

  15. }

  16. },

  17. {

  18. "name" : "cat",

  19. "data" :

  20. {

  21. "stringValue" : document.getElementById("cat_e_" + assetId).value

  22. }

  23. }

  24. ],

  25. "name" : document.getElementById("name_e_" + assetId).value,

  26. "description" : document.getElementById("desc_e_" + assetId).value,

  27. // This should be removed.

  28. "publist" : "${config.csSiteName}"

  29. };

  30. // Convert JSON data to string.

  31. var strdata = JSON.stringify(data);

  32. // Perform AJAX request.

  33. var req = getXmlHttpObject();

  34. req.onreadystatechange = function ()

  35. {

  36. if (req.readyState == 4)

  37. {

  38. if (req.status == 200)

  39. {

  40. // On successful result

  41. // update the view controls with new values and switch the mode to 'view'.

  42. for (c in controls)

  43. {

  44. document.getElementById(controls[c] + "_v_" + assetId).innerHTML =

  45. document.getElementById(controls[c] + "_e_" + assetId).value;

  46. }

  47. switchMode(assetId, false);

  48. }

  49. else

  50. {

  51. // Error happened or the session timed out,

  52. // reload the current page to re-acquire the session.

  53. alert("Failed to call " + assetUrl + ", " + req.status + " " + req.statusText);

  54. window.location.reload( false );

  55. }

  56. }

  57. };

  58. // We put Content-Type and Accept headers

  59. // to tell CS REST API which format we are posting

  60. // and which one we are expecting to get.

  61. req.open("POST", assetUrl, true);

  62. req.setRequestHeader("Content-Type", "application/json;charset=utf-8");

  63. req.setRequestHeader("Content-Length", strdata.length);

  64. req.setRequestHeader("Accept", "application/json");

  65. req.send(strdata);

  66. }

72.3.2 Making REST Calls from Java

The code below (in HomeController.java) calls the assets search service to list all assets of type FW_Article. The code uses the Jersey Client library passing objects from the rest-api-<version>.jar library provided by the WEM Framework. This way we leverage strong typing in Java.

It is important to note that a token must be acquired from Java code by calling the SSOAssertion.get().createToken() method. It is unnecessary to do so in JavaScript as that side is already authenticated against WEM SSO.

// Use Jersey client to query CS assets.
Client client = Client.create();
String url = config.getRestUrl() + "/types/FW_Article/search";
WebResource res = client.resource( url );
                
// Construct URL and add token (for authentication purposes)
// and fields (specify which fields to retrieve back) parameters.        
res = res.queryParam("fields", URLEncoder.encode("name,description,content,cat,source", "UTF-8"));
res = res.queryParam("ticket", SSO.getSSOSession().getTicket(res.getURI().toString(), config.getCsUsername(), config.getCsPassword()));
// Put Pragma: auth-redirect=false to avoid redirects to the CAS login page.
Builder bld = res.header("Pragma", "auth-redirect=false");
        
// Make a network call.
AssetsBean assets = bld.get(AssetsBean.class);

Note:

The custom Pragma: auth-redirect=false header instructs the CAS SSO filter not to redirect to the CAS sign-in page, but to return a 403 error instead, when no ticket is supplied or the supplied ticket is invalid.

72.4 Constructing URLs to Serve Binary Data

The "Articles" application leverages the Blob server in WebCenter Sites to serve BLOB data. The following utility function could be used to construct the URL pointing to the binary data for a given attribute in a given asset, where blobUrl points to the Blob server (http://localhost:8080/cs/BlobServer by default).

public String getBlobUrl(String assetType, String assetId, String attrName, String contentType)
throws Exception
{
String contentTypeEnc = URLEncoder.encode(contentType, "UTF-8");
        
return blobUrl + "?" +
"blobkey=id" + 
"&blobnocache=true" +
"&blobcol=thumbnail" +
"&blobwhere=" + assetId +
"&blobtable=" + assetType +
"&blobheader=" + contentTypeEnc +
"&blobheadername1=content-type" +
"&blobheadervalue1=" + contentTypeEnc;
    }

An alternative way to get binary data is to load an asset using the resource /sites/{sitename}/types/{assettype}/assets/{id}. When loaded, the asset will contain the URL pointing to the BLOB server.

72.5 Context Object: Accessing Parameters from the WEM Framework

The UI container provides a JavaScript Context object (WemContext) to all applications inside the container. The Context object is used by the applications to get details from the WEM Framework about the logged-in user and site (typically, to get the current site's name from the UI container). The Context object also provides various utility methods that the applications will use to share data. The Context Object can be used by applications running in the same domain as WebCenter Sites or in different domains.

Note:

The wemcontext.html file lists the exposed methods, summarized in Section 72.5.3, "Methods Available in Context Object."

This section contains the following topics:

72.5.1 Same Domain Implementations

To initialize and use Context Object for applications in the WebCenter Sites domain:

  1. Include wemcontext.js (line 1 in the sample code below; wemcontext.js is located in <cs webapp path>/wemresources/js/WemContext.js).

  2. Retrieve an instance of the WemContext object (line 3).

  3. Use the methods of WemContext (lines 4 and 5).

Example 72-1 Sample Code for Same-Domain Implementations

  1. <script src='http://<csinstalldomain>/<contextpath>/wemresources/js/WemContext.js'></script>

  2. <script type="text/javascript">

  3. var wemContext = WemContext.getInstance(); // Instantiate Context Object

  4. var siteName = wemContext.getSiteName(); // Get Site Name

  5. var userName = wemContext.getUserName(); // Get UserName

  6. </script>

72.5.2 Cross-Domain Implementations

To initialize and use Context Object for cross-domain applications:

  1. Copy wemxdm.js, json2.js, and hash.html (from the Misc/Samples folder) to your application.

  2. Open the sample.html file and make the following changes to perform cross-domain calls:

    1. Change the paths of wemxdm.js and json.js and hash.html to their paths in the application (see lines 1 through 4 in the code below).

    2. Change the path of wemcontext.html to its location in WebCenter Sites (wemcontext.html is located under /wemresources/wemcontext.html. Use the WebCenter Sites host name and context path. See line 14.)

    3. In the interface declaration, specify methods that will be used in the framework (line 15).

    4. Implement those methods in the local scope and invoke the remote method (line 30).

Example 72-2 sample.html for Cross-Domain Calls

  1. <script type="text/javascript" src="../js/wemxdm.js"></script>

  2. <script type="text/javascript">

  3. // Request the use of the JSON object

  4. WemXDM.ImportJSON("../js/json2.js");

  5. var remote;

  6. window.onload = function() {

  7. // When the window is finished loading start setting up the interface

  8. remote = WemXDM.Interface(/** The channel configuration */

  9. {

  10. // Register the url to hash.html.

  11. local: "../hash.html",

  12. // Register the url to the remote interface

  13. remote: "http://localhost:8080/cs/wemresources/wemcontext.html"

  14. }, /** The interface configuration */

  15. {

  16. remote: {

  17. getSiteName :{},

  18. ...

  19. }

  20. },/**The onReady handler*/ function(){

  21. // This function will be loaded as soon as the page is loaded

  22. populateAttributes();

  23. });

  24. }

  25. </script>

  26. <script type="text/javascript">

  27. /** Define local methods for accessing remote methods */

  28. function getSiteName(){

  29. remote.getSiteName(function(result){

  30. alert("result = " + result);

  31. });

  32. }

  33. ...

  34. </script>

72.5.3 Methods Available in Context Object

Table 72-1 Methods Available in Context Object

Return Type Method name and Description

Object

getAttribute(attributename)

Returns attribute value for the given attribute name.

Object

getAttributeNames()

Returns all the attribute names.

Object

getCookie(name)

Returns cookie value for the given name. Has all restrictions of the normal browser cookie.

Object

getCookies()

Returns all the cookies.

Object

getLocale()

Returns locale.

Object

getSiteId()

Returns the site id.

Object

getSiteName()

Returns the site name.

Object

getUser()

Returns user object.

Object

getUserName()

Returns user name.

void

removeCookie(name, properties)

Removes cookie.

void

setAttribute(attributename, attributevalue)

Sets attribute. These attributes can be accessed in other applications.

void

setCookie(name,value,expiredays,properties)

Sets the cookie.


72.6 Registration Code

Registration exposes applications in the WEM Framework, as explained in Section 70.5, "Authorization Model." Registering an application creates an asset of type FW_Application and an asset of type FW_View for each view associated with the application. The asset types are enabled on AdminSite. Their attributes are defined in the Oracle Fusion Middleware WebCenter Sites REST API Bean Reference. Programmatic registration is the preferred method. For an example of manual registration, see Chapter 78, "WEM Framework: Registering Applications Manually."

This section contains the following topics:

72.6.1 Registering Applications with an iframe View

The section uses code from the "Articles" sample application to illustrate the registration process. "Articles" has a single view of type iframe. The same steps apply to JavaScript and HTML views.

To register an application

  1. Create or get an icon to represent your application. (The icon will be displayed in the applications bar.)

    (The "Articles" sample application uses the articles.png image file located in: /sample app/articles/src/main/webapp/images/)

  2. Create a file that specifies the layout of the application in HTML, that is, for each view, create a placeholder element to hold the content rendered by the view. Applications and views are related as shown in Figure 72-4.

    For example, layout.jsp for the "Articles" sample application contains the following line:

<div id="articles" style="float:left;height:100%;width:100%" class="wemholder"></div>

The view's content will be rendered within the placeholder element when the application is displayed (layout.app renders the application's layout; home.app renders the view).

Note:

When creating the layout file, specify a unique id for the placeholder element. You will specify the same id for the parentnode attribute when coding the view object. Use class="wemholder" for the placeholder elements.

Figure 72-4 Applications and Views

Description of Figure 72-4 follows
Description of "Figure 72-4 Applications and Views "

The relationship between applications and views is many-to-many (Figure 72-4). One application can have multiple views and each view can be used by many applications. Only registered views can be shared (through their asset IDs). If the asset ID is omitted, the view will be created within the context of its application. In the basic case, an application has only one view associated with it.

  1. Invoke the PUT wem/applications/{applicationid} REST service and specify your application bean. Populate the bean with the view asset and application asset.

    For an iframe view, use the code of the "Articles" sample application, i.e., InstallController.java (locate the comment lines // Create a new view object and // Create a new application object). Set the layouturl attribute to specify the URL of the application's layout page.

    In the "Articles" application, the layouturl attribute points to the URL of layout.app (implemented by LayoutController.java):

    app.setLayouturl(config.getArticlesUrl() + "/layout.app");
    

You can test the results of your registration process by logging in to the WEM Admin interface as a general administrator and selecting Apps on the menu bar. Your application should be listed on that page.

72.6.2 Registering Applications with JavaScript and HTML Views

For applications that use HTML and JavaScript views, follow the steps in the previous section, but use the sample code and attributes listed below:

72.6.2.1 JavaScript View

Note:

JavaScript specified in the view will be rendered (executed) when the application is rendered. Make sure that the JavaScript does not conflict with other views.

Sample code:

   window.onload = function () { 
      if (GBrowserIsCompatible()) { 
        var map = new GMap2(document.getElementById("map_canvas")); 
        map.setCenter(new GLatLng(37.4419, -122.1419), 13); 
        map.setUIToDefault(); 
      } 
   }
  • Rendering the JavaScript view from a source URL

    Set the following attributes:

    • name: Name of the view

    • parentnode: ID of the placeholder element (from step 2 in Section 72.6.1, "Registering Applications with an iframe View") .

    • viewtype: fw.wem.framework.ScriptRenderer, which renders JavaScript into the placeholder element.

    • sourceurl: Path of the .js file, which provides content for the view. For example: http://example.com:8080/js/drawTree.js

  • Rendering the JavaScript view from source code

    Set the following attributes:

    • name: Name of the view

    • parentnode: ID of the placeholder element (from step 2 in Section 72.6.1, "Registering Applications with an iframe View").

    • viewtype: fw.wem.framework.ScriptRenderer, which renders JavaScript into the placeholder element

    • javascriptcontent: JavaScript code (sample provided above. The code must not contain <script> tags.)

72.6.2.2 HTML View

Note:

HTML specified in the view will be rendered (executed) when the application is rendered.

Sample code:

<object width="480" height="385">
 <param name="movie" value="http://www.localhost:8080/jspx/flash_slider_main.swf"></param>
 <param name="allowFullScreen" value="true"></param>
 <embed src=" http://www.localhost:8080/jspx/flash_slider_main.swf"
  type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true"
  width="480" height="385">
 </embed>
</object>
  • Rendering the HTML view from a source URL

    Set the following attributes:

    • name: Name of the view

    • parentnode: ID of the placeholder element (from step 2 in Section 72.6.1, "Registering Applications with an iframe View").

    • viewtype: fw.wem.framework.IncludeRenderer, which renders JavaScript into the placeholder element

    • sourceurl: Path to the HTML file that provides content for the view. For example: http://example.com:8080/js/drawTree.jsp

  • Rendering the HTML view from source code

    Set the following attributes:

    • view: Name of the view

    • parentnode: ID of the placeholder element (from step 2 in Section 72.6.1, "Registering Applications with an iframe View").

    • viewtype: fw.wem.framework.IncludeRenderer, which renders JavaScript into the placeholder element

    • includecontent: HTML content (sample provided above. The code must not contain <html> or <body> tags.