Table of Contents [-]
- 1 Introduction
- 2 Creating an OpenCAF Portlet Application Project
- 3 Creating an OpenCAF Application Using AngularJS and Bootstrap
- 3.1 Prerequisites
- 3.2 AngularJS Overview
- 3.3 Bootstrap Overview (For Responsive Web Design)
- 3.3.1 Showing Specific Bootstrap Components using AngularJS
- 3.4 Single Page Application (SPA Applications)
- 3.5 The SCOPE Object
- 3.6 General Design
- 3.7 Example
- 3.7.1 Example Application Structure
- 3.8 Adding External Libraries for AngularJS Application
- 3.9 Adding Directives
- 3.10 Designing Layout
- 3.11 Starting and Registering Application
- 3.12 Loading Chart
- 3.13 Invoking REST Service
- 3.14 Creating Custom Directive
- 3.15 Adding Routing To Application
- 3.16 Authenticating RESTful Services
- 3.17 Integrating CAF Controls
- 3.17.1 Integrating OpenCAF Controls
- 3.17.2 Reading Value from OpenCAF
- 3.17.3 Registering callbacks
- 3.18 Integrating Bootstrap
- 3.19 Integrating MWS Resources
- 3.19.1 Approach #1: Using AngularJSs ng-include
- 3.19.2 Approach #2: Using JSF include
- 3.20 Creating Re-usable Generic Components
- 3.20.1 Creating jQuery Plugins
- 3.20.2 Using Directives in Angular JS
- 3.21 Creating REST APIs in MWS
- 3.21.1 Approach #1
- 3.21.2 Approach #2
- 3.22 Sample Project Screenshots
- 3.22.1 Sample Layout
- 3.22.2 Sample MWS Resources
- 3.23 See Also
- 4 Authenticating Remote RESTful Services Using SAML
- 5 Related content:
Introduction #
This article provides information on how to quickly create the following OpenCAF applications:
- OpenCAF portlet application project
- OpenCAF application using AngularJS and Bootstrap
The sample project explained in this article is based on the architecture used in Business Console.
Creating an OpenCAF Portlet Application Project #
Perform the following steps to create a portlet application project using OpenCAF.
- Go to Designer.
- Open File -> New -> Portlet Application Project.
- Click Next.
\
4. Provide a name for the project in the Project name field.
5. In the Initial Project Contents section, select the following:Project Template: OpenCAF ShellInitial Content: Custom Application Page With Empty Framing
6. To modify the default values, like changing the alias or the index page, click "Modify“ under "Initital Project Contents“ and then proceed to Step 7.If you want to go by the defaults, click Finish. A new dynamic web application is created.
7. Edit the alias listed under <executeCommand> tag in the /WEB-INF/shells/page_xhtml.xml XML configuration fileFor example, if you have specified OpenCAFApplication in the Project name field, the alias for the application will be app.OpenCAFApplication.
8. Rename the home/index page of the application, if required. By default, the home/index page of the application is called page.xhtml. The page.xhtml file is located directly under the WebContent directory. If you would like to change the default page name, update the ‘Filename’ input to whatever name you would like to have.
9. Launch the application in a browser by typing http://localhost:8585/app.OpenCAFApplication. The application will appear empty because no pages are developed yet.
Creating an OpenCAF Application Using AngularJS and Bootstrap #
Prerequisites #
- Knowledge of AngularJS and Bootstrap
- External libraries for AngularJS application
AngularJS Overview #
AngularJS is one of the latest frameworks that provide core client-side functionalities. Developers find this framework most suitable for SPAs (Single-paged applications).
AngularJS follows a declarative approach to integrate seamlessly with HTML declarations (as against an imperative approach of many other frameworks such as JQuery).
Bootstrap Overview (For Responsive Web Design) #
Bootstrap framework developed by the Twitter team addresses many problems related to rendering HTML pages on multiple devices (desktop screens, tablets, mobile phones) without the need to create separate application for each resolution. Bootstrap leverages on the CSS3 based media queries to provide a 12 column responsive layout, which adapts to the rendering resolution.
Bootstrap is a very popular open source HTML, CSS, and JS framework for building responsive web designs. You can create panels by specifying the width and the number of columns. For example, if you want to divide a page equally with two panels layout, then each division must have 6 cols (col-md-6) width, where ‘md’ means medium devices.
Bootstrap also enables you to define the same view for mobile screens. A division that takes 50% width in desktop screen, will take 100% width in a mobile or tablet view. This can be specified using col-xs-12 (xs means extra small devices). A general three column layout is shown in the image below.
Bootstrap also provides several re-usable components such as modal dialogs, accordions, and buttons. These components can be customized by overriding the specific CSS.For example, a bootstrap modal dialog follows the following structure.
<div class="modal " id="sample-modal-id">
<div class="modal-dialog" >
<div class="modal-content ">
<div class="modal-header"></div> // Modal Header
<div class="modal-body"></div> // Modal Body
<div class="modal-footer"></div> // Modal Footer
</div>
</div>
</div>
Note: For overriding the default CSS, never modify the original bootsrap JS or CSS. This will enable you to seamlessly upgrade the bootstrap version as and when newer versions are released. If your application has similar behavior or look and feel for specific components, you can override the base styles in a base CSS, and use specific CSS to override specific components. For example, in the code sample above, you can override classes modal, modal-dialog, modal-content, modal-header, modal-body, and modal-footer. This will impact all modals in an application. To have specific override , use the #modal-id in the style as shown below.
.modal{
….. (this impacts all modals)
}
#sample-modal-id .modal{
…… (this impacts modals with id sample-modal-id)
}
Showing Specific Bootstrap Components using AngularJS #
Most of the bootstrap components can directly work with AngularJS in a declarative approach without much use of JavaScript. However, there might be a situation when declarative approach does not work, and you might have to trigger the elements using jQuery imperatively.
For example, to use bootstrap popovers and listen for specific popover events such as ‘shown.bs.popover’, triggered as soon as the element is displayed, you cannot achieve this with just HTML, you need to use JavaScript for event listening. Event handling can be easily done in AngularJS using custom directives.
The ‘element’ parameter in the Custom Directive is a jQLite object (which is a subset of jQuery). Event handlers can then be attached to the ‘element’ as shown below.
element.on(' shown.bs.popover’ ', function () {
do something…
})
Note: If jQuery is available, angular.element is an alias for the jQuery function. If jQuery is not available, angular.element delegates to the Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
Single Page Application (SPA Applications) #
A single page application (SPA) is a web application that fits on a single web page with the goal of providing a more fluid user experience similar to a desktop application. In a SPA, all the necessary code – HTML, JavaScript, and CSS are retrieved with either a single page load or are dynamically loaded and added to the page as necessary (lazy loading), usually in response to user actions.
SPA does not reload at any point in the process, nor does it transfer control to another page in the process. However, with AngularJS (and other modern frameworks) and by using Routing , content of the current page can be replaced by the content of another page (without the need to reload static files such as JavaScripts, CSS, images). Due to this, a SPA performs much faster as compared to traditional multi-page applications.
In terms of client-side scripting, SPA might require complex coding compared to multi-page applications. AngularJS framework provides excellent support for developing SPA, specially because of the data-binding capabilities with the UI Model.
The SCOPE Object #
Before you start the application design, understand the inbuilt Angular object called SCOPE. AngularJS uses multiple instances of a SCOPE object within an application (one per controller/directive instance). SCOPE is a JavaScript object that works similar to a JavaScript’s prototype object.
Angular SCOPE can be used to hold any JavaScript object or function. They are hierarchical in nature and are in-line with the DOM structure of the application. If you are using nested controllers in an application, a function defined in a parent SCOPE can be invoked from a function defined in any child SCOPE, but NOT vice versa. SCOPEs are important objects for carrying the model information. However, for best practice, SCOPE objects should not be overloaded with variables. You should rather create an angular service, update data in the service object, and then load the object in the controller. This helps to keep the application clean and to minimize the errors.
General Design #
AngularJS provides a way to create SPA applications. The entire application must to be enclosed within an ng-app or data-ng-app directive (for more information on directives, refer https://docs.angularjs.org/guide/directive ).
AngularJS applications follow the client-side MVC (Model-View-Controller) architecture. The View is the HTML section, the Controller is a JS file that handles data binding of the View, and the Model can be any JS object that can be tied to the View.
The role of the Controller is to manage data change of the Model objects reflected in the View. Controller functions can be triggered in several ways. It can be invoked by user action (for example, data-ngclick) or a rendering event (for example, data-ng-repeat). AngularJS provides several directives with trigger controller function. For large applications, it is better to have multiple controllers and split the event controls.
At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children. Angular comes with a set of built-in directives such as ngBind, ngModel, and ngClass. Just like you create controllers and services, you can create your own directives for Angular usage. When Angular bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements.
Example #
The example shows you how to:
- Create a generic AngularJS application
- Make the application use the OpenCAF components/resources
- Make the application responsive and device-friendly by using Bootstrap
Example Application Structure #
- External libraries are placed in ext directory.
- Client-side JS files are in WebContent /scripts directory. Note: Imports to all the required JS and CSS files are added to the index.xhtml file using <script> and <link> tag respectively.
- Application is within the data-ng-app="OpenCAFApp" directive placed within the first div in the index.xhtml page.
- Layout contains two columns with a sidebar menu on the left, and the body content on the right.
- Uses a single controller - AppController. Note: To use a controller, create a .js file called AppController.js in the WebContent/scripts directory. Add the required imports for the AppController.js file in the index.xhtml file.
Adding External Libraries for AngularJS Application #
- Create ext directory under WebContent directory.
- Add the following files to the WebContent/ext directory.
- JavaScript (.js) and CSS files required for AngularJS
- JQuery libraries
- Bootstrap libraries, if required
You can download the library files required for AngularJS, JQuery, and Bootstrap from the following locations. - AngularJS: https://angularjs.org/
- JQuery: https://jquery.com/download/
- Bootstrap: http://getbootstrap.com/getting-started/#download
For charts, you can use the nvd3.js charting library.oNVD3: http://nvd3.org/
Adding Directives #
Enclose the entire application within an ng-app (or data-ng-app) directive (for more information on directives refer to https://docs.angularjs.org/guide/directive .)
In the example, the application is within the data-ng-app="OpenCAFApp" directive.
- Directive is placed in the first div in the index.xhtml page.
- External libraries are placed in ext directory.
- Client-side JS files are placed in WebContent /scripts directory.
Designing Layout #
Browse the templates available in the bootstrap website (http://getbootstrap.com/getting-started/). You can use a start-up template and customize it, or create your own template.
In the example application, the layout has two columns. A sidebar menu on the left, and the body content on the right.
Starting and Registering Application #
To start the application, you need to register the application as an AngularJS module.- Add a file called app.js inside the script directory, and add the required imports.
- Use the code below for registering the example application:
var openCAFApp = angular.module('OpenCAFApp', [ 'ngRoute']);
where ngRoute is a module which is required for an application to handle routing. This module handles controller invocation on browser URL change and to change the content of a page based on the URL.
Loading Chart #
To load a chart based on the sample JSON data retrieved from a REST invocation, you need angular services. Use a service for REST invocation, and a custom directive to load the chart at the specific HTML tag.
Invoking REST Service #
To create a REST service for chart, do the following:
- Create a service to handle the REST invocation.
- Create a JS file called RestService.js and place it in the /WebContent/scripts/services directory.
- Import the RestService.js file in the index.xhtml file.
- Add the following code to the file.
In the example, an instance of Angular’s ‘$http’ service is used to invoke a REST call tothe URL: /data/pie_chart.json. This is a local JSON file, but it can be replaced with any REST URL to provide the required data. RestServiceProvider() has an invoke method with the following arguments:
Argument | Description |
URLobj | URL of the REST Service to be invoked. JSON format:{url:’the url string ’,method:'GET/POST/PUT/DELETE',isArray:true/false} |
successCallback | Callback function to be invoked after successful REST invocation. |
errorCallback | Callback function to be invoked if there is an error in the REST invocation. |
parameters | URL parameters to be passed. Parameters will be converted to this format:parameters URL parameters to be passed. Parameters will be converted to this format:url?key1=value1&key2=value2… |
data | Request data to be passed in case of POST or PUT invocations. |
scope(optional) | scope(optional) Scope of the invocation object. Scope will be passed back along with the success/error callback functions. |
pathParam | Additional path parameters to be appened to the URL before the parameters. |
Go to 'Sample project for OpenCAF - Invoking REST Service' in our code samples library
Creating Custom Directive #
To create a custom directive called ‘piechart’, do the follwoing:
- Create a JS file (piechart_directive.js) under the /WebContent/scripts/directives directory.
- Import the piechart_directive.js file in index.xhtml.
- Add the code below in the piechart_directive.js file.
Note: Angular’s dependency injection is used to inject an instance of the RestService created in the previous step.
To use the piechart directive, place the directive piechart=”piechart” as an attribute in the HTML place holder.
Pie chart loads as follows:
- AngularJS loads the HTML page and detects a directive called ‘piechart’.
- The ‘link’ function of the directive is invoked with an instance of the <svg> tag as element
\argument.<svg id="pie_chart" piechart="piechart"></svg> - Angular uses the RestService object to ‘invoke’ the REST URL.
- In success callback, nvd3 functions are invoked with the data obtained to load the required chart.
Go to 'Sample project for OpenCAF - Creating Custom Directive' in our code samples library//
Adding Routing To Application #
In a SPA application, you might require routing to handle the page content based on URL.
In the example application, to add routing to load pages on click of menu on the side-bar, do the following:
- Enable routing in app.js.
var openCAFApp = angular.module('OpenCAFApp', [ 'ngRoute']);
2. In app.js, declare the routes and their corresponding URLs as follows:
openCAFApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/tables/', {
controller: 'AppController',
templateUrl: '/opencafapplication/pages/tables.xhtml',
activetab: 'tables'
}) .when('/', {
controller: 'AppController',
templateUrl: '/opencafapplication/pages/dashboard.xhtml',
activetab: 'dash'
}).
when('/forms', {
controller: 'AppController',
templateUrl: '/opencafapplication/pages/forms.xhtml',
activetab: 'dash'
}).
when('/myinbox', {
controller: 'AppController',
templateUrl: '/opencafapplication/pages/resources.xhtml',
activetab: 'myinbox'
}).
otherwise({
redirectTo: '/',
templateUrl: '/opencafapplication/pages/dashboard.xhtml',
activetab: 'dash'
});
window.routeProvider = $routeProvider;
}]);
The code snippet above specifies four different routes based on the URL pattern. For example , if the URL is http://localhost:8585/app.OpenCAFApplication/forms, it will load forms.xhtml.
The ng-view directive indicates the place for loading the XHTML to initiate the routing process. Hence, in index.html, specify a placeholder as follows:
<div class="container-fluid">
<div ng-view="ng-view" ></div>
</div>
When angular encounters the ng-view directive, it will look up the route information in app.js and based on the current URL, it will load the corresponding XHTML from the location specified in the ng-view tag.
Authenticating RESTful Services #
If AngularJS UI and REST Components are deployed on the same MWS, then no additional configurations are required to authenticate the services. MWS will automatically authenticate all the RESTful services once the user logs into MWS.
If REST components and AngujarJS UI reside on separate MWS, but share a common directory interface, then you need to authenticate the RESTful services using SAML. For more information, see [Authenticating Remote RESTful Services Using SAML].
Integrating CAF Controls #
Integrating OpenCAF Controls #
Import the appropriate tags in the HTML block as shown below:
<html xmlns="[[http://www.w3.org/1999/xhtml|http://www.w3.org/1999/xhtml]]"
xmlns:caf_f="[[http://webmethods.com/jsf/caf/core|http://webmethods.com/jsf/caf/core]]"
xmlns:caf_h="[[http://webmethods.com/jsf/caf/html|http://webmethods.com/jsf/caf/html]]"
xmlns:f="[[http://java.sun.com/jsf/core|http://java.sun.com/jsf/core]]"
xmlns:mws_h="[[http://webmethods.com/jsf/mws/html|http://webmethods.com/jsf/mws/html]]"
xmlns:ui="[[http://java.sun.com/jsf/facelets">|http://java.sun.com/jsf/facelets">]]
Reading Value from OpenCAF #
ControlTo read any value from the OpenCAF control, the best way is to populate the value into a hidden HTML field wrapped around a hide able panel, and then use JavaScript to read the value directly.
For example, if you are using the PeoplePicker control, the selected value is written into a hidden field with "selectedHidden" ID. This value can then be read into the application by attaching a click event on the "Apply" button on the PeoplePicker control.
<caf_f:view pageManagedBean="DefaultView">
<caf_h:form defaultFocus="_first" id="defaultForm">
<mws_h:peoplePickerDialog id="peoplePickerDialog"
refineSearchButtonAvailable="false"
targetValue="#{DefaultView.selectedValue}"
refreshOnApply="peoplePickerHiddenPanel">
<f:facet name="popupPanel" />
</mws_h:peoplePickerDialog>
<caf_h:panelHideable visible="false" id="peoplePickerHiddenPanel">
<input type="hidden" id="selectedHidden"
value="#{DefaultView.selectedValue}"
onchange="alert('#{DefaultView.selectedValue}')" />
</caf_h:panelHideable>
</caf_h:form>
</caf_f:view>
Registering callbacks #
Because OpenCAF provides external components, there is no direct way to register callbacks on specific event trigger within the OpenCAF control. In case PeoplePicker control, even though a hidden field is populated when you click Apply, there is no way to know when the field was populated. You must attach an on-click event for the Apply button. The code snippet below shows how to do this.
CAF.model("jsf:defaultForm:peoplePickerDialog:applyButton").element.onclick=function()
{ //Attach an event function to handle click on "Apply" button//
setTimeout(function(){
var modal=CAF.model("jsf:defaultForm:peoplePickerDialog");
if(modal){
modal.hide();
//Since we are overriding the original click event, we need to hide the//
//control manually here//
}
},100);
CAF.model(this).go();
var taskId = CAF.model("jsf:defaultForm:peoplePickerDialog").
element.getAttribute("taskId");
//Extra attributes have been set on the control prior to launch//
var timeout = setInterval(function() {
if (typeof jQuery("#selectedHidden").val() !="undefined") {
//The selected value is retireved from the Hidden Field//
/* DO SOMETHING */
clearInterval(timeout);
}
}, 100);
//We need a setInterval here since it takes some time for OpenCAF to populate t//he hidden field. So on-click of Apply it will check if the value is available//and then act on it.//
};
Integrating Bootstrap #
OpenCAF is based on the UI framework called Prototype.js. However, Prototype is not guaranteed to be compatible with other UI frameworks such as JQuery or Bootstrap. This is because Prototype.js extends the core JS functionalities and overrides jQuery or Bootstrap. jQuery/Bootstrap uses a completely encapsulated code.
To run jQuery along with Prototype, use Prototype in a no-conflict mode using jQuery.noConflict(). Prototype uses the '$' variable internally, so jQuery cannot use the $ function to invoke it. jQuery selectors must be invoked using 'jQuery' keywork instead of '$'. Besides this, Prototype.js extends the DOM directly instead of creating a DOM wrapper like most modern JS frameworks use. This is another common cause of conflicts outside of using ‘$’ internally that jQuery uses by default.
Disable all the bootstrap functions in Prototype before using it, because Prototype conflicts with several bootstrap functions. Use the code below to disable the bootstrap functions:
if (Prototype.BrowserFeatures.ElementExtensions) {
var disablePrototypeJS = function (method, pluginsToDisable) {
var handler = function (event) {
event.target[method] = undefined;
setTimeout(function () {
delete event.target[method];
}, 0);
};
pluginsToDisable.each(function (plugin) {
jQuery(window).on(method + '.bs.' + plugin, handler);
});
},
pluginsToDisable = ['collapse', 'dropdown', 'modal', 'tooltip','popover'];
disablePrototypeJS('show', pluginsToDisable);
disablePrototypeJS('hide', pluginsToDisable);
}
Integrating MWS Resources #
There are various ways to integrate the MWS resources in an OpenCAF application. The code snippets are with reference to Business Console. Business Console uses MWS resources primarily for:
- Starting a Task Instance
- Viewing Business Data
Business Console loads its various components through partial XHTMLs. Hence, the MWS Resources are included in partial HTMLs. In Business Console, the shell templates are created, loaded, and included by using the partial HTMLs of the MWS resources.
The shell templates for partial HTMLs are created as shown below:
<folder name="StartTask" userAlias="folder.appeditor.startTask">
<page>
<row>
<column width="100%">
</column>
</row>
</page>
</folder>
<wm_xt_shell name="appeditor.startTask" template="/socialBPM/pages/tlm/starttask.xhtml"
parent="shell.power_drill" userAlias="shell.startTask" />
<executeCommand commandName="addwebspace" webspaceID="folder.appeditor.startTask"
alias="business.console.startTask" />
Note: The shell templates were created because Business Console is a CAF-based web application. There were a few issues while accessing the MWS resources for getting the context information.
Both the approaches below worked for including these shell templates in Business Console.
Approach #1: Using AngularJSs ng-include #
Use this approach when it is not required to pass context to the included resource.
When you use AngularJS ng-include, the parameter passed does not properly get bound to JSF bind-ing if the target resource is CAF/JSF resource, but should work fine with other AngularJS or other type of resources.
ng-include
<div class="modal fade bs-modal-lg" id="startInstantTask" tabindex="-1"
role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" style="startInstantTaskStyle">
<div class="modal-content">
<div class="modal-header bc-gray-bg bc-modal-header cus-modal-header">
Start Task
<img class="close-icon pull-right" src="/socialBPM/images/common/close-icon-std-14px.png" data-dismiss="modal"/>
</div>
<div class="modal-body">
<div data-ng-include="currentStartInstantTaskURL"></div>
</div>
<div class="modal-footer bc-gray-bg">
<button type="button" class="bc-button start-task-close bc-button-link"
id="start-task-close" data-dismiss="modal" >Cancel</button>
</div>
</div>
</div>
</div>
Approach #2: Using JSF include #
Use this approach if the included resource is a CAF or JSF resource, and you need to pass the context information.
JSF Include
****<div class="tab-content">
<ui:include src="taskdetailsBusinessDataTab.xhtml">
<ui:param name="taskInstanceID" value="#{param['taskID']}" />
<ui:param name="taskPortlet"
value="#{param['taskAppName']}___#{param['taskTypeName']}" />
<ui:param name="taskAppName" value="#{param['taskAppName']}" />
<ui:param name="taskTypeName" value="#{param['taskTypeName']}" />
<ui:param name="time" value="#{param['time']}" />
</ui:include>
</div>
Creating Re-usable Generic Components #
You might often want to create generic components for re-use across teams with minimum code change. Although Bootstrap provides several generic UI components that can be used directly such as modals and accordions, you have to customize them for a specific look and feel before re-using these customized UIs.
Two ways to create generic UI components:
- Creating jQuery plugins
- Using Directives in AngularJS
Creating jQuery Plugins #
This is the most generic way to create components that are independent of an UI framework. jQuery Plugins are created by accessing jQuery’s prototype object $.fn. For more details, see http://learn.jquery.com/plugins/basic-plugin-creation/ .
Using Directives in Angular JS #
When you want to create an AngularJS application, it is more convenient to create custom directives which manipulate the DOM directly without the need of jQuery plugins. However, you might use jQuery utility functions for DOM manipulation, if you are re-using component in your application.
Consideration for creating custom directives in AngularJS for creating generic components:
- Always create the directive as part of an independent module. For example, angular.module('OpenCAFApplication.dialog',[]). directive(…….) and then associate it with your module in app.js as shown below.
angular.module('MODULE_NAME', ['ngRoute','OpenCAFApplication.dialog'])
Never try to directly associate it with your module . For example if your module name is ‘module1’, do not use module1.directive(……) - Provide a way for users to customize the component. As Angular follows a declarative approach, the only possible way to do this is by providing attributes .For example, if you are creating a generic button component , then you might need to provide ways to the users to have dynamic display names of the button based on a scope variable. Also provide users with callbacks such as onclick.
- If you are generating HTML dynamically in a directive, make sure the generated code is brought under AngualrJS context using the $compile service. The $compile service can be injected into your directive directly. For example,
$compile(generated_element_object)(scope)
Creating REST APIs in MWS #
Use one of the following approaches to expose REST APIs in MWS by using Jersey. Refer the Jersey API documentation for further details.
Approach #1 #
- Create a web application project in Designer.
- Implement your REST resource and application classes using the Jersey APIs in the web application project.
- Configure Jersey in web.xml.
- Deploy the web application on MWS.
Approach #2 #
- Implement your REST resource and application classes using the Jersey API's in a Standalone Java project.
- Bundle the classes in a jar file.
- Copy your jar file to the softwareag/MWS/lib folder and then run the "softwareag/MWS/bin/mws.[bat|sh] update" command.
\NOTE: If your jar needs some custom instructions (for example, the jar needs to be a fragment of another bundle), you can place the jar_filename_without_jar_extension.bnd file in the same folder as the jar file. When the .bnd file is not present, your custom bnd instructions will be used instead of using the simple defaults.For example, if you have a library that must be attached as a fragment to another bundle, you can edit the jar_filename_without_jar_extension.bnd file as shown:
# attach as fragment to the caf.server bundle Fragment-Host: com.webmethods.caf.server - Update the "restApplication.properties" properties file and add the entry for your application class. Use MWS’s “getconfig” and “putconfig” to update properties file.
- Restart MWS.
Sample Project Screenshots #
Sample Layout #
Sample MWS Resources #
See Also #
Sample OpenCAF project attached to this article.
To use the attached application, follow the steps below:
- Extract the zip file to a local directory.
- Copy the extracted OpenCAFApplication.war to <SoftwareAG>/MWS/server/<servername>/deploy directory. E.g C:/SoftwareAG/MWS/server/default/deploy/.
- Alternatively, you can import the extracted war to Desiger and then deploy it to MWS.
Authenticating Remote RESTful Services Using SAML #
The configuration in this section is applicable only when the REST components and AngujarJS UI are residing on separate MWS, and sharing a common directory interface.
To ensure that the application user is authenticated by MWS before invoking any REST invocation, do the following:
- Use an http incterceptor to check if the ‘restAuthenticated’ object has been set to true. If not, the incoming request must be pushed to a ‘requests401’ object, and an event ‘event:loginRequired’ must be fired.
- Add an event handler to retrieve the SAML token using ‘/socialBPM/uirest/SAMLToken ’ and then fire a POST call to ‘/rest/authenticate’ by passing the SAML token as the request data. If the invocation is successful, the initial REST invocation must be retrieved from the 'requests401' object and then must retire the invocation.
- Set the ‘restAuthenticated’ object to true to avoid reauthentication request.
Go to 'Authenticating Remote RESTful Services Using SAML' in our code samples library
In the code snippet above, if the ‘restAuthenticated’ object is false,
- Create an object called ‘request401’ and push the request in ‘request401’.
- Trigger an event called 'event:loginRequired'.
- A listener for this event must invoke login retries by a POST call using a SAML token objtained from ‘/rest/authenticate/’ URL.
openCAFApp.run(['$rootScope', '$http', function(scope, $http) {
scope.$on('event:loginConfirmed', function() {
var i, requests = scope.requests401;
for (i = 0; i < requests.length; i++) {
retry(requests[i]);
}
scope.requests401 = [];
function retry(req) {
$http(req.config).success(req.config.successHandle)
.error(req.config.errorHandle);
}
});
scope.$on('event:loginRequired', function() {
var authURL = '<AUTHENTICATION_URL>';
var isRemote = true;
}
$http({
method: REST_URLS.SAML_TOKEN.method,
url: REST_URLS.SAML_TOKEN.url
})
.success(function(response) {
response = response.replace(/\+/g, '%2B');
response = response.replace(/\=/g, '%3D');
$http({
method: 'POST',
url: authURL,
withCredentials: true,
data: "SAMLResponse=" + response,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.success(function() {
if (isRemote) {
remoteRESTAuthenticated = true;
} else {
restAuthenticated = true;
}
scope.$broadcast('event:loginConfirmed');
})
.error(
function() {
if (isRemote) {
remoteRESTAuthenticated = true;
} else {
restAuthenticated = true;
}
scope.$broadcast('event:loginConfirmed');
});
});
});
}]);