2011년 12월 5일 월요일

작업항목 속성사용자정의 - DataSourceValueSet

https://jazz.net/wiki/bin/view/Main/DataSourceValueSetProviders

Since version 3.0.1.1 and 3.5 it is possible to configure a Value Set customization that fetches the values for an attribute using an external data source such as an HTTP Rest request that fetches XML data.




Overview

The current implementation of the Data Source Value Set providers consists of the following key pieces:
  • a extension to Value Set Provider to access data sources. An additional filter string can be used to limit the number of values fetched.
  • an implementation of a Filtered Value Set Provider - the HTTP Filtered Value Set Provider that is available as a data set provider and that can be parametrized with URLs and XPaths to access data sets from XML documents available with HTTP.
  • new presentation widgets a (Combo box and a Value Picker) that can be configured for attributes of any string type. These widgets allow the user to choose the value of the string attribute based on the configured value set providers.
  • a extended API to implement custom value set providers that can use data source and use filter capabilities
  • a Data Source framework that allows new data connector objects to be contributed and used inside value providers.
  • an implementation of Data Source Connector - the HttpConnector. This class can be used inside Value Set Providers (both in Java and Javascript) to fetch an XML document from any online service.

The HTTP Filtered Value Set Provider

The HTTP Filtered Value Set Provider is part of the product and allows the user to fetch data sets from XML documents that can be fetched using HTTP.
  • The provider reads the XML data
  • The XML document is transformed in a table of strings using XPath expressions for rows and columns
  • From each row a value set entry is created, using a format pattern that describes how to construct the label from the column entries
  • Optionally, the data set sentries are sorted and filtered to match the filter string. If the XML data is already filtered and sorted this step is not necessary
To use the HTTP Filtered Value Set Provider, go to the Attribute Customization page in the process spec editor and create a new value set provider of type HTTP Filtered Value Set.
The example uses a car database that returns data the following way:

  
    1
    Acura
    ,1,2,
  
  
    2
    Aixam
    ,2,
  
...

Configure the provider
  1. XML data set URL. This resource is expected to return a XML document: For example: http://cars.flashmx.us/makes
  2. Row XPath expression: Selects a list of nodes from the XML document: //xml/node
  3. Column XPath expression(s): Expressions relative to each node found to get the labels for each column (in this example: 1 column). ./make.
  4. Column Identifiers: A name for each column: Make
  5. Output Format: A string template that describes how to construct the label based on the number of the column: ${0}
  6. Select to fiter and sort the entries. Our data set does not do filtering and sorting for us, so we need to do this in the provider
Example using more than one column expression:
  1. URL: http://cars.flashmx.us/makes
  2. Row XPath expression: //xml/node
  3. Column XPath expressions: ./make|./id.
  4. Column Identifiers: Make|Id
  5. Output Format: ${0} (${1})
  6. Select to fiter and sort the entries.
In 3.5 M4 the URL and the Row XPath expression additionally support the following variables:
  1. ${filter} the current filter
  2. ${xy.value} the value of attribute 'xy'. The value is the internal representation of an attribute value, e.g. 'severity.literal.l5'
  3. ${xy.label} the label of attribute 'xy'. The label is the display name of the attribute value, e.g. 'Critical'
Example using variables
  1. URL: http://cars.flashmx.us/makes
  2. Row XPath expression: //xml/node[starts-with(./make, '${filter}')]
  3. Column XPath expressions: ./make.
  4. Column Identifiers: Make
  5. Output Format: ${0}
  6. Select to fiter and sort the entries.
You can then configure the new provider as a value set provider to a string attribute (small, medium and large strings).

Work Item Editor Presentations

To take advantage of the new value set provider mechanisms you must use define a string attribute and specify a presentation that supports the new functionality. Currently these are theValue Set Combo Box and the Value Set Picker presentations. They work with small, medium and large strings.

Value Set Combo Box

This presentation simply displays a combo box that allows the user to choose any value from the value set associated with the attribute. Both a filtered and an unfiltered value set providers are supported, but when used with a filtered value set provider the filter is an empty string.
In both the Eclipse and the Web clients the values for the combo box are fetched in the background every time an item is opened or refreshed.
This presentation should be used with caution when combined with a value set provider that uses a data source. Since this presentation does not use a filter it is only suited for smaller value sets. Using it with value set providers that return a large set might hurt performance.

Value Set Picker

This presentation displays a text field, a button to clear the text and a button to bring out a value set selection dialog:
As soon as a filter string is typed in the dialog the Value set service will be invoked to retrieve the corresponding value set. You can then select any element from the list. Optionally when configuring this presentation you can specify that it should allow any input string. In this case you can directly modify the value in the work item editor and do not need to use the dialog.
This presentation works with both IValueSetProivder and IFilteredValueSetProvider customizations. In the first case the filter string provided by the user is not used.

Query Editor Presentations

The Value Set Picker presentation is also available when building queries. In that case you can always directly edit the string and do not need to use the dialog. It is important to note that currently the presentation in the Query editor works only with short and medium strings.
As of yet there is no UI to configure query presentations and this needs to be done manually in the project configuration. Here is how to configure the special Value Set Picker presentation for an attribute called productPart:
<configuration-data id="com.ibm.team.workitem.query.editor.configuration" xmlns="http://com.ibm.team.workitem.query.editor/configuration">
    <query-type id="com.ibm.team.workitem.workItemType">
        <presentations>
            <presentation attributeId="productPart" kind="com.ibm.team.workitem.kind.internal.valueset"/>
        </presentations>
    </query-type>
</configuration-data>
On the client side there are two situations in which the new mechanism to fetch value sets is invoked:
  • If a work item is open that contains a Value Set Combo presentation, then the value set for this combo is automatically fetched in the background.
  • If the user opens a picker dialog to choose a value. This dialog can be opened in the work item editor or in the query editor when an attribute is configured with the Value Set Pickerpresentation.
Fetching a value set is done using a service call to RTC server which uses the value set provider defined for the selected attribute. Within the value set provider it is possible to create a connection to an external data source using a Data source connector. Such a connector can further use the Authentication service to retrieve user credentials and use them when establishing connections.
In case of an error the Value Set Service will return more information about the problem.
To take advantage of this functionality you must at a minimum implement a Java/Javascript Value set provider. Your provider can use the existing Data Connectors and Attribute Presentations. It is also possible to contribute your own Data Connectors.
The different features are explained in more detail in the sections below.

Custom Filtered Value Set Providers

A new filtered Value Set Provider API was introduced which takes a filter string as an additional input. This string can be used to limit the number of values that need to be fetched from a remote server.
To use this in Java implement the new com.ibm.team.workitem.common.internal.attributeValueProviders.IFilteredValueSetProvider interface which extends the existingcom.ibm.team.workitem.common.internal.attributeValueProviders.IValueSetProvider by adding an extra method getFilteredValueSet(). This method includes all the arguments of getValueSet() plus an additional filter string. Typically calls to the old getValueSet() method should be translated to calls to getFilteredValueSet() using an empty filter.
The use this in Javascript you can write a file using this skeleton:
dojo.provide("org.example.FilteredValueSet");

(function() {
dojo.declare("org.example.FilteredValueSet", null, {

    getFilteredValueSet: function(attributeId, workItem, context, filter) {
        //...
    }
});
})();
Important: If your Javascript file implements the getFilteredValueSet() method make sure to tick the Filtered checkbox in the provider configuration in Eclipse:

Data source connectors

Data source connectors are contributed classes which can be used to fetch data from an external source such a REST service or a database. A Data Connector must implement the marker interface com.ibm.team.workitem.api.common.connectors.IDataConnector. It is encouraged that responses from such connectors are objects implementingcom.ibm.team.workitem.api.common.connectors.IResponse which provides a simple way to iterate over response entries. However it is possible to return a response object of a different type.
To contribute a new data connector you must extend the dataConnector extension point defined in com.ibm.team.workitem.service. Each contribution must specify a unique id. Inside a Value Set provider you can get a new instance of any contributed data connector by calling:
  • For Javascript: context.getDataConnector("connectorId")
  • For Java: workItemCommon.getDataConnector("connectorId")
Furthermore to make a new connector contribution available in Javascript it should implement an interface that is exported for the Script Engine used in the RTC server. To export classes from a project to Javascript, the project needs to extend the com.ibm.team.rtc.common.scriptengine.apiBundle extension point:
  • To export all classes from the project simply use:
    <extension point="com.ibm.team.rtc.common.scriptengine.apiBundle" />
  • To export specific classes configure the extension point with additional include child elements:
    <include pattern="someRegEx"/>
    The pattern attribute specifies a regular expression. Classes which match this expression will be exported.
Currently there is a single Data connector available the - HttpConnector

The IResponse and IReponseEntry interfaces

The com.ibm.team.workitem.api.common.connectors.IResponse interface represents a simple iterator concept with the following two methods:
  • boolean hasNext() returns whether there are more entries in the response
  • IReponseEntry next() returns the next response entry or null if there are no more entries
The com.ibm.team.workitem.api.common.connectors.IResponseEntry interface supports the following operations:
  • int size() returns the number of fields in this entry
  • String getByPosition(int position) returns the value of field at the specified position
  • String getById(String id) returns the value of the field with the specified id
  • String getIdForPosition(int position) returns the id of the field at the specified position

HTTP Connector

The Http Connector allows the fetching of data from a REST service. It supports the following features:
  • Form-based and basic authentication. Credentials can be supplied directly or using the Authentication service.
  • Fetching XML data
  • Querying XML using XPath to form the final response.
The connector is implemented in the com.ibm.team.workitem.service.internal.connector.HttpConnector class and has a single public method IResponse get(HttpConnectorParameters params) throws ConnectorException. Calling this method will initiate an HTTP connection, authenticate, fetch the XML data pointed to by the URL, use XPath to form the response and return it. All of these actions are determined by the params argument.
HttpConnectorParameters:
  • public String url required The url to connect to
  • public boolean ignoreInvalidCertificates whether to ignore invalid SSL certificates when connecting via HTTPS
  • public String userName the user name to use if authentication is required. Note that this will only be used if credentialsId is not provided.
  • public String password the password to use if authentication is required. Note that this will only be used if credentialsId is not provided.
  • public String credentialsId the id of the credentials to use if authentication is required. The user name and password corresponding the credentials id will be provided by theAuthentication service.
  • public boolean useBasicAuthentication whether or not to use basic authentication
  • public String formBasedAuthenticationLoginURL the URL to use for form-based authentication. This is only used if useBasicAuthentication is false. Form-based authentication will be performed using a GET request.
  • public String formBasedAuthenticationUsernameInputName the parameter name that specifies the user name for form-based authentication
  • public String formBasedAuthenticationPasswordInputName the parameter name that specifies the password for form-based authentication
  • public String xpath required An XPath expression that will be used to compute a list of nodes that will appear in the response.
  • public String[] columnIds Used to more precisely define how each entry of the response should look like. If this array is not null each response entry will have columns with ids equal to the elements of the array.
  • public String[] columnXpaths Used to more precisely define how each entry of the response should look like. The size of this array must always be equal to the size of columnIds. Each element of the array defines how the value of the corresponding column in the entry will be retrieved.
To customize the response you need to define the xpathcolumnIds and columnXpaths arguments: Here is how they work:
  1. After the document pointed to by the URL is fetched the xpath argument is used to get a list of matching nodes.
  2. For each of those nodes a response entry will be created. The columnXpaths argument is used to run additional queries that return the values of individual columns.
  3. Each such value is inserted into the current response entry and is given the corresponding id from columnIds.
In case you do not provide the columnIds and columnXpaths arguments, each response entry will be formed in this way:
  1. After the document pointed to by the URL is fetched the xpath argument is used to get a list of matching nodes.
  2. For each of those nodes a response entry will be created. All values of attributes and all text values that are part of the node will appear as a separate column in the response entry. Here is an example node and the corresponding entry:
<book language="English">
  <title>The Lord of the Rings</title>
  <author name="J. R. R. Tolkien" born="1892"/>
</book>
book.attribute_languagebook.titlebook.author.attribute_namebook.author.attribute_born
EnglishThe Lord of the RingsJ. R. R. Tolkien1892
If any exception is encountered during the operation of the HttpConnector it will be wrapped inside an ConnectorException and this new exception will be thrown.

Authentication Service

Data connectors can use the com.ibm.team.workitem.service.internal.connector.IAuthenticationService service to retrieve the credentials that correspond to a particular id. The idea of this service is that sensitive login information is not stored in plain text in a Javascript file or in the process configuration but is rather stored in a secure way on the server. The current implementation of the Authentication service is very basic and only partially fulfils this requirement.
Currently credentials supplied by the Authentication service can be configured in the Advanced properties page in the server configuration. Modify the property Login Credentials for Data Connectors in the section com.ibm.team.workitem.service.internal.connector.AuthenticationService.
Credentials are stored in a string value using a simple encoding. Here is an example:
credentialID1|||username1|||password1###ID2|||bob|||bobspassword###someotherCredential|||jane|||secret
  • Different credentials are separated by ###
  • Different parts of a credential are separated by |||. A credential has three parts in this order:
    • ID - this is what plug-ins should provide to the Authentication service in order to receive the corresponding user name and password.
    • User name
    • Password

Tutorial for providing a custom value set provider

Here is a step-by-step example that will show you how to implement an configure a custom Value Set Provider that has the same functionality as the provided HTTP filtered value set provider.

  1. Using the Eclipse client open the project configuration and go to the Project Configuration > Configuration Data > Work Items > Attribute Customization section.
  2. Create a new Script-Based Value Set Customization. Call it Google Movies:
  3. Use the following Javascript code:
    dojo.provide("org.example.workitems.providers.GoogleMoviesValueSet");
    dojo.require("com.ibm.team.workitem.api.common.connectors.HttpConnectorParameters");
    
    (function() {
    var HttpConnectorParameters= com.ibm.team.workitem.api.common.connectors.HttpConnectorParameters;
    
    dojo.declare("org.example.workitems.providers.GoogleMoviesValueSet", null, {
    
        getFilteredValueSet: function(attributeId, workItem, context, filter) {
            
            var params= new HttpConnectorParameters();
            params.url= "http://www.google.com/ig/api?movies=zurich";
            params.xpath= "//movie";
            params.columnXpaths= ["./title/@data"];
            params.columnIds= ["Title"];
            
            var connector= context.getDataConnector("HttpConnector");
            var values= connector.get(params);
            
            var result= [];
            while(values.hasNext()){
                var entry= values.next();
                var title= entry.getById("Title");
                if (title.indexOf(filter) > -1) {
                    result.push(title);
                }
            }
            
            return result;
        }
    });
    })();
    
    This script will use the HttpConnector to connect to a Google API and fetch some data. Using the specified XPath expressions only the titles from the returned XML are extracted. Any title that includes the filter string will be returned in the final value set.
    Note: The used API only return a maximum of three movies.
  4. Tick the Filtered check-box to indicate that this Value Set Provider supports a filter. The final configuration should look similar to this:
  5. Go to the Project Configuration > Configuration Data > Work Items > Types and Attributes section.
  6. Create a new medium string attribute. Call it Current Movie and select the Google Movies value set provider:
  7. Go to the Project Configuration > Configuration Data > Work Items > Editor Presentations section.
  8. Select the appropriate section and click Add Presentation. Configure the presentation like the screen shot below:
  9. (Optional) Configure a presentation for the Query editor. See here: Query Editor Presentations.
  10. Save the configuration.
  11. Create a new work item to see the new attribute and use it.
    • You can directly type a movie title:
    • You can click the Select value button and choose a value from the list

Implementation notes

The notes below outline some implementation details that might be of interest to RTC developers who will extend the functionality of the new Value Set mechanism.

Use of IFilteredValueSetProvider

Internally, in almost all cases only IValueSetProvider is used. There are only two cases where an instance of IValueSetProvider is cast to IFilteredValueSetProvider:
  • in the Value Set Service. In that service the type of the value set provider is evaluated. If it is IFilteredValueSetProvider then that interface is used, otherwise the standardIValueSetProvider will be used. When the provider is implemented in Javascript it can always be cast to IFilteredValueSetProvider and therefore there is a secondary check described next.
  • in the script ScriptAttributeValueProvider class. If the process configuration indicates that a Javascript file implements a filtered value set provider and the getFilteredValueSet()method was called then the filtering interface will be used. To indicate that a script implements IFilteredValueSetProvider simply check the Filtered checkbox when uploading the javascript in the Eclipse client.

Value set service

The Value Set Service consists of two interfaces:
  • For the Eclipse Client: com.ibm.team.workitem.common.internal.IValueSetService
  • For the Web: com.ibm.team.workitem.common.internal.rest.IValueSetRestService
This service provides a single method that returns a ValueSetDTO object containing status information and a value set. To call the service you need to provide a project id, an attribute id and optionally an item type, an item id, and additional item attributes that have been changed in the working copy and are needed to compute the value set.
Calling the service without a work item id is used from within the query editor. Calling the service with an item type and no item id is used when creating a new work item.

Authentication Service improvement

The current implementation of the Authentication service needs further development and can be found in the com.ibm.team.workitem.service.internal.connector.AuthenticationService class.
TODO: Implement a more secure mechanism for supplying and storing credentials. A good way to do this is to store encrypted credentials inside a process attachment file and provide a GUI for manipulating this file.

Providing a built-in value set provider that uses the HTTP Connector

It is possible to ship RTC with a ready to use Value Set Provider that uses an HTTP Connector. There are two main parts to doing this:
  • Implement a Javascript provider like usual but make sure to read all parameters for the HttpConnectorParameters structure from the provider's XML configuration.
  • Implement a GUI that simply modifies the project configuration and provides the necessary parameters.
This is already done for attribute customizations such as the Regular Expression and Number Range validators. The best way to see how this is done is to refer to thecom.ibm.team.workitem.shared.common project and more specifically the com.ibm.team.workitem.shared.common.internal.providers package inside. Here are the highlights:
  • To deploy a Javascript file on the server you need to extend the com.ibm.team.rtc.common.scriptengine.scriptBundles extension point.
  • If the Javascript should be available in a Web Browser also extend the net.jazz.ajax.webBundles extension point.
  • To declare the new provider extend attributeValueProviders in the following way:
    <extension point="com.ibm.team.workitem.common.attributeValueProviders">
       <valueSetProvider
         class="com.ibm.team.workitem.shared.common.internal.valueProviders.ScriptAttributeValueProvider"
         id="org.example.HttpFilteredValueSetProvier"
         name="HTTP Filtered Value Set Provider"
         icon="icons/obj16/script_obj.gif">
       <script class="org.example.HttpFilteredValueSetProvier"/>
       <attributeType id="smallString"/>
       <attributeType id="mediumString"/>
       <attributeType id="string"/>
      </valueSetProvider>
    </extension>
    Note that the class attribute of valueSetProvider must refer to ScriptAttributeValueProvider.
  • Additional effort might be required to successfully attach scripts. Have a look at com.ibm.team.workitem.shared.common.internal.
  • To create a GUI for configuring the provider use this extension point:
    <extension point="com.ibm.team.workitem.ide.ui.valueProviderAspectlets">
        <aspectlet
              class="org.example.aspectlets.HttpFilteredValueSetProvierAspectlet"
              providerId="org.example.HttpFilteredValueSetProvier"
              providerType="valueSetProvider" />
    </extension>
    This is used a lot in com.ibm.team.workitem.ide.ui. Have a look there for more information on how to implement the GUI controls.

댓글 없음:

댓글 쓰기