ATG Rest Web Services ATG Rest Web Services Manny Parasirakis Software Architect ATG 6

W
Document Sample
scope of work template
							ATG Rest Web Services

                   Manny Parasirakis
              Software Architect - ATG
                            6/23/2009
Agenda
 What are REST Web Services?
 What are ATG REST Web Services?
 Security
 Filtering and Aliasing
 Client Libraries
Q&A




                           2
What are REST Web Services?




                 3
What are REST Web Services?
 Stands for “Representational State Transfer”
 Not a standard
 It is an architectural style




                          4
What are REST Web Services?
 Everything on the web is a “resource”
   – http://www.boeing.com/aircraft
   – http://www.boeing.com/aircraft/747
   – http://www.boeing.com/aircraft/747/engine2
 HTTP method type determines what operation to
 execute on a resource
   –   GET
   –   POST
   –   PUT
   –   DELETE


                        5
What are REST Web Services?
 http://www.boeing.com/aircraft (GET)
   –{
        “aircraft”: [
            http://www.boeing.com/aircraft/707,
            http://www.boeing.com/aircraft/717,
            http://www.boeing.com/aircraft/727,
            http://www.boeing.com/aircraft/737,
            http://www.boeing.com/aircraft/747,
            http://www.boeing.com/aircraft/757,
            http://www.boeing.com/aircraft/767,
            http://www.boeing.com/aircraft/777,
            http://www.boeing.com/aircraft/787
        ]
      }


                          6
What are REST Web Services?
 http://www.boeing.com/aircraft/777 (GET)
   –{
       “name”: “Boeing 777”,
       “seats”: 538,
       “engines”: 2,
       “engine-manufacturer”: “General Electric”,
       “engine-specs”:
     “http://www.boeing.com/aircraft/777/engine-specs”
     }




                        7
What are REST Web Services?
 http://www.boeing.com/aircraft/777/takeoff (POST)

   {
       “atgResponse”: true
   }




                             8
What are ATG REST Web Services?




                 9
What are ATG REST Web Services?
 Allows access to ATG resources to data and
 functionality over http
   –   Read/Write nucleus component properties
   –   Execute nucleus component methods
   –   Execute form handler methods
   –   Create/Read/Update/Delete repository items and
       their properties
 Exposes all the fundamental operations and
 access to data that is required to build an ATG
 application


                           10
New Paradigm? Maybe…




               11
How do you enable ATG REST Web Services
 Add the REST module to the list of modules at startup
 This will enable access in a RESTful way to all nucleus
  components, form handlers, and repositories
   – Yes, there is a security layer
 If a new component, property, or method is added, the only
  change needed to access that resource via REST is to
  configure security to allow access




                              12
Bean/Repository REST Requests
 Two types of ATG REST requests
   – Bean
   – Repository
 Bean request is used to call nucleus component
 methods, form handler methods, and access
 nucleus component properties
 Repository request is used to execute repository
 operations and access repository data
   –   Create
   –   Read
   –   Update
   –   Delete
                       13
What does an ATG REST Request look like?
 scheme://server:port/rest/[bean|repository]/
    <resource path>




                        14
What does an ATG REST Request look like?
 Bean requests
 GET requests (Retrieve nucleus component data)
   – http://server:port/rest/bean/atg/dynamo/Configuration
   – http://server:port/rest/bean/atg/dynamo/Configuration/httpPort
 POST requests (Call a nucleus or form handler method)
   – http://server:port/rest/bean/atg/dynamo/Configuration/setHttp
     Port
 PUT (Set a nucleus component property)
   – http://server:port/rest/bean/atg/dynamo/Configuration/httpPort




                              15
What does an ATG REST Request look like?
 Repository requests
 GET requests
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product/abc123
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product/abc123/displayName
 POST requests
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product/abc123
 PUT
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product/abc123/displayName
 DELETE
   – http://server:port/rest/repository/atg/commerce/catalog/ProductCatal
     og/product/abc123
                                16
What do the responses look like?
 Responses can be either JSON or XML
   – Default is defined on server
   – Client can override per request
 {
      “httpPort”: 8840
  }
 <atgResponse>
   <httpPort>8840</httpPort>
  </atgResponse>




                          17
More Response Examples (JSON)
{
    "allRootCategories": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/allRootCategories",
    "allRootCategoryIds": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/allRootCategoryIds",
    "ancestorCatalogsAndSelf":
        "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/ancestorCatalogsAndSelf",
    "ancestorCategories": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/ancestorCategories",
    "directAncestorCatalogsAndSelf":
        "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/directAncestorCatalogsAndSelf",
    "directParentCatalogs": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/directParentCatalogs",
    "displayName": "Tools",
    "displayName_de": null,
    "displayName_en": "Tools",
    "displayName_ja": null,
    "id": "cat123",
    "index": 0,
    "itemAcl": null,
    "indirectAncestorCatalogs":
        "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/indirectAncestorCatalogs",
    "parentFolders": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/parentFolders",
    "repositoryId": "cat123",
    "rootCategories": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootCategories",
    "rootCategoryIds": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootCategoryIds",
    "rootSubCatalogs": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootSubCatalogs",
    "status": "other",
    "subCatalogIds": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/subCatalogIds",
    "subCatalogs": "http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/subCatalogs",
}



                                                                18
More Response Examples (XML)
<atgResponse>
  <repositoryId>cat123</repositoryId>
  <parentFolders>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/parentFolders</parentFolders>
  <subCatalogIds>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/subCatalogIds</subCatalogIds>
  <rootCategories>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootCategories</rootCategories>
  <ancestorCategories>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/ancestorCategories</ancestorCategories>
  <status>other</status>
  <indirectAncestorCatalogs>
     http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/indirectAncestorCatalogs
     </indirectAncestorCatalogs>
  <ancestorCatalogsAndSelf>
     http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/ancestorCatalogsAndSelf
     </ancestorCatalogsAndSelf>
  <displayName/>Tools</displayName>
  <displayName_en>Tools</displayName_en>
  <index>0</index>
  <id>cat123</id>
  <rootSubCatalogs>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootSubCatalogs</rootSubCatalogs>
  <subCatalogs>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/subCatalogs</subCatalogs>
  <allRootCategoryIds>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/allRootCategoryIds</allRootCategoryIds>
  <itemAcl/>
  <allRootCategories>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/allRootCategories</allRootCategories>
  <displayName_ja/>
  <rootCategoryIds>http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/rootCategoryIds</rootCategoryIds>
  <displayName_de/>
  <directAncestorCatalogsAndSelf>
     http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/directAncestorCatalogsAndSelf
     </directAncestorCatalogsAndSelf>
  <directParentCatalogs>
     http://localhost/rest/repository/atg/commerce/catalog/ProductCatalog/catalog/cat123/directParentCatalogs
     </directParentCatalogs>
</atgResponse>




                                                                 19
Response Codes
   200 (OK) - The Request was successful. For a GET request, the response contains an entity
    corresponding to the requested resource. In a POST request the response will contain an entity
    describing or containing the result of the action.
   201 (Created) - Returned only for POST requests that create repository items. The request was
    successful and the repository item was created.
   400 (Bad Request) - The request could not be completed because the request URL and/or
    parameters were improperly formatted.
   401 (Unauthorized) - user session does not have the proper security credentials to execute the
    method, property, or access the repository for the requested resource.
   403 (Forbidden) - user has configured that a specific property is not writable via the filtering
    configuration.
   404 (Not Found) - The request could not be completed because it was made for a resource which
    does not exist.
   410 (Gone) - Returned only for DELETE requests that remove repository items. The request was
    successful and the repository item was deleted.
   500 (Internal server error) - The request could not be completed because an unexpected exception
    occurred.




                                                  20
ATG REST Security




                    21
ATG REST Security
 Requires client to have a user profile and be logged in
 to access functionality by default
   – Default can be changed to not require login
 Once logged in no access to resources is allowed by
 default
 Must configure security to access resources




                          22
ATG REST Security
 Uses ATG security system, acls
 Security can be placed on any resource
   – Nucleus component (read, write)
   – Nucleus component property (read, write)
   – Nucleus component method (execute)
   – Form handler method (execute)




                        23
ATG REST Security
 Repository security is used for securing repository
 items
   – No special REST security for repository items
     exists
 Unsecure repositories are not allowed any access
 by default




                         24
ATG REST Security
 Security is configured via an XML file in the
 configpath
   – /atg/rest/security/restSecurityConfiguration.xml




                         25
ATG REST Security
 Set security on all components, their methods and properties
    – <rest-security>
          <default-acl value="Profile$login$user1:read,write,execute"/>
        </rest-security>
 Set security on specific methods
    – <rest-security>
         <resource component="/atg/userprofiling/ProfileServices">
           <default-acl value="Profile$login$user1:read,write,execute"/>
           <method name="loginUser" secure="false“/>
           <method name="logoutUser" secure="false“/>
         </resource>
         <resource component="/atg/userprofiling/ProfileFormHandler">
           <method name="handleLogin" secure="false“/>
           <method name="handleLogout" secure="false“/>
         </resource>
        </rest-security>
 Set security on specific component properties
    – <rest-security>
         <resource component="/atg/rest/Configuration">
           <default-acl value="Profile$login$user1:read,write,execute"/>
           <property name="defaultOutputCustomizer" secure="false“/>
           <property name="defaultInputCustomizer" secure="false“/>
         </resource>
        </rest-security>




                                           26
ATG REST Security
 Set security on all components beginning with a certain path
   – <rest-security>
        <resource component="/atg/commerce/*">
          <default-acl value="Profile$login$user1:read,write,execute"/>
        </resource>
       </rest-security>




                                      27
Filtering and Aliasing




                    28
Filtering and Aliasing
 Component or repository item properties can be
 marked as hidden or not writeable
 Configure subset of properties to be returned for
 components or repository items
 Add additional virtual properties to components or
 repository items
 Create virtual components which allows for custom
 “objects”




                          29
Filtering and Aliasing
 Filtering is configured via an XML file in the
 configpath
   – /atg/rest/filtering/filteringConfiguration.xml




                           30
Filtering and Aliasing
 Example of one hidden property and another writable in a nucleus
  component and a repository item

   – <rest-filtering>
      <component name="/some/Component" default-include="true">
       <property name="property1" hidden="true"/>
       <property name="property2" writable="false"/>
      </component>

       <component name="/some/Repository" default-include="true">
         <item-descriptor name=“itemDescriptorName">
          <property name="repProperty1" hidden="true"/>
          <property name="repProperty2" writable="false"/>
         </item-descriptor>
       </component>
      </rest-filtering>




                              31
Filtering and Aliasing
 The default-include attribute tells the system whether or not to
  include the native properties defined in the component or
  repository item or just the ones listed in the configuration file

    – <rest-filtering>
       <component name="/some/Component" default-include="true">
        <property name="property1" hidden="true"/>
        <property name="property2" writable="false"/>
       </component>

       <component name="/some/Repository" default-include="true">
         <item-descriptor name=“itemDescriptorName">
          <property name="repProperty1" hidden="true"/>
          <property name="repProperty2" writable="false"/>
         </item-descriptor>
       </component>
      </rest-filtering>


                                 32
Filtering and Aliasing
 Additional properties which do not exist in the nucleus component or repository
  item can be added as virtual properties
 The configuration also contains the information about the source of the values of
  the virtual properties
     – <rest-filtering>
        <component name="/some/Component" default-include="true">
          <property name=“label" target=“displayName"/>
          <property name=“color" target=“specs.color"/>
        </component>
        <component name="/some/Repository" default-include="true">
          <item-descriptor name=“itemDescriptorName">
           <property name=“label" target=“displayName"/>
           <property name=“color" target=“specs.color"/>
          </item-descriptor>
        </component>
       </rest-filtering>




                                      33
Filtering and Aliasing
 Adding a component attribute to the property tag and using that in combination
  with the target tag, the value of a property can come from another nucleus
  component
 Dot notation can be used in the target attribute

 <rest-filtering>
    <component name="/some/Component" default-include="true">
     <property name=“price" component="/some/other/Component" target=“price"/>
    </component>

   <component name="/some/Repository" default-include="true">
     <item-descriptor name=“itemDescriptorName">
      <property name=“price" component="/some/other/Component" target=“price"/>
     </item-descriptor>
   </component>
  </rest-filtering>




                                       34
Filtering and Aliasing
 If you need to write custom code to specify your value, then you must use the
  property-customizer attribute
 <rest-filtering>
    <component name="/some/Component" default-include="true">
     <property name=“manufacturer" property-customizer="some.class.Here"/>
    </component>

   <component name="/some/Repository" default-include="true">
     <item-descriptor name=“itemDescriptorName">
      <property name="manufacturer" property-customizer="some.class.Here"/>
     </item-descriptor>
   </component>
  </rest-filtering>
 atg.rest.filtering.RestPropertyCustomizer interface
     – public Object getPropertyValue(String pPropertyName, Object pResource);
     – public void setPropertyValue(String pPropertyName, Object pValue, Object
       pResource);


                                      35
Filtering and Aliasing
 One thing which can be done is to specify the name of a component
  which does not exist in the name attribute of the component tag
 When this component is requested, the list of properties which is
  specified inside the component tag will be rendered
 Only use the property tag with the component/target attributes or the
  property-customizer attribute
 <rest-filtering>
   <component name="/some/nonexisting/Component">
    <property name="property1" component="/some/other/Component“
                                 target="aProperty"/>
    <property name="property2" property-customizer="some.class.Here"/>
   </component>
  <rest-filtering>




                                  36
ATG REST Client Libraries




                  37
ATG REST Client Libraries
 There are two client libraries which makes using ATG REST
  Web Services very easy
   – Java
   – Actionscript (Flex/Flash)
 Hides all the complexity of ATG REST Web Services
 Exposes methods to
   – Read/Write Nucleus component property values
   – Execute Nucleus component and form handler methods
   – Create/Read/Update/Delete repository items and properties
   – Query repositories/Execute RQL queries




                                 38
Java Client Lib
 RestSession
 RestComponentHelper
 RestRepositoryHelper
 RestResult
 RestConstants
 RestClientException




                         39
Java Client Lib Example
public RestSession login() throws RestClientException {
  RestSession session = RestSession.createSession(getHostname(),
                   getPort(), getUsername(), getPassword());
  session.setUseHttpsForLogin(false);
  session.login();
  return session;
}

public void logout(RestSession pSession) throws RestClientException {
 if (pSession != null)
   pSession.logout();
}




                               40
Java Client Lib Example – Get Component
RestSession session = null;
RestResult result = null;

try {
  session = login();
  result = RestComponentHelper.getComponent("/atg/dynamo/Configuration", null, session);
  String res = result.readInputStream();

 // do something with the result (either JSON or XML formatted)
}
catch (RestClientException e) {
  e.printStackTrace();
  fail(e.toString());
}
catch (IOException e) {
  e.printStackTrace();
  fail(e.toString());
}
finally {
  if (result != null)
    result.close();
  logout(session);
}
                                                41
Java Client Lib Example – Get Property Value
 RestResult result =
  RestComponentHelper.getPropertyValue("/atg/dynamo/Configuration", “httpPort",
  null, session);




                                   42
Java Client Lib Example – Set Property Value
 RestResult result =
  RestComponentHelper.setPropertyValue("/atg/dynamo/Configuration", “httpPort",
  8080, session);




                                    43
Java Client Lib Example – Execute Method
 RestResult result =
  RestComponentHelper.executeMethod("/some/nucleus/component",
  “methodName", new Object [] {10, “string”}, null, session);
 Map<String,Object> params = new HashMap<String,Object>();
  params.put(RestConstants.METHOD, “(I;Ljava/lang/String)V”);
  RestResult result =
  RestComponentHelper.executeMethod("/some/nucleus/component",
  “methodName", new Object [] {10, “string”}, params, session);




                                  44
Java Client Lib Example – Execute Form Handler
Method
 Map<String,Object> params = new HashMap<String,Object>();
  params.put("firstName", "Andy");
  params.put("lastName", "Jones");
  RestResult result =
  RestComponentHelper.executeMethod("/some/FormHandler", "create", null,
  params, session);




                                   45
Java Client Lib Example - Parameters
   No parameters
      – RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                          “methodName", null, session);

   Simple parameter
      – RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                          “methodName", new Object[] {10}, session);
      – RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                          “methodName", new Object[] {“string”}, session);
      – RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                          “methodName", new Object[] {10, “string”},
         session);

   Collection parameter
      – List<Integer> list = new ArrayList<Integer>();
          list.add(10);
          RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                              “methodName", new Object[] {list}, session);

   Object parameter
      – MyObject myObject = new MyObject();
          RestResult result = RestComponentHelper.executeMethod("/some/nucleus/component",
                                            “methodName", new Object[] {myObject}, session);


                                          46
Java Client Lib Example – Get Repository Item
 RestResult result =
  RestRepositoryHelper.getItem("/atg/commerce/catalog/ProductCatalog", “product",
  “prod12345”, null, session);




                                    47
Java Client Lib Example – Get Repository Items
 RestResult result =
  RestRepositoryHelper.getItems("/atg/commerce/catalog/ProductCatalog",
  “product", null, session);




                                   48
Java Client Lib Example – Execute RQL Query
 RestRepositoryHelper.executeRQLQuery("/atg/commerce/catalog/ProductCatalog“
  , “product", “ALL”, null, session);
 Map<String,Object> params = new HashMap<String,Object>();
  params.put(RestConstants.INDEX, 0);
  params.put(RestConstants.COUNT, 10);
  RestResult result =
  RestRepositoryHelper.executeRQLQuery("/atg/commerce/catalog/ProductCatalog“
  , “product", “ALL”, params, session);




                                        49
Java Client Lib Example – Get Repository
Property Value
 RestResult result =
  RestRepositoryHelper.getPropertyValue("/atg/commerce/catalog/ProductCatalog",
  “product", “prod12345”, “displayName”, null, session);




                                   50
Java Client Lib Example – Set Repository
Property Value
 RestResult result =
  RestRepositoryHelper.setPropertyValue("/atg/commerce/catalog/ProductCatalog",
  “product", “prod12345”, “displayName”, “My Product Name”, null, session);




                                   51
Java Client Lib Example – Create Repository
Item
 RestResult result =
  RestRepositoryHelper.createItem("/atg/commerce/catalog/ProductCatalog",
  “product", null, session);
 RestResult result =
  RestRepositoryHelper.createItem("/atg/commerce/catalog/ProductCatalog",
  “product", “myProductId12345”, null, session);
 Map<String,Object> params = new HashMap<String,Object>();
  params.put(RestConstants.TRANSIENT, true);
  RestResult result =
  RestRepositoryHelper.createItem("/atg/commerce/catalog/ProductCatalog",
  “product", params, session);




                                   52
Java Client Lib Example – Remove Repository
Item
 RestResult result =
  RestRepositoryHelper.removeItem("/atg/commerce/catalog/ProductCatalog",
  “product", “prod12345”, null, session);




                                   53
Rest Parameters
 atg.rest.client.RestConstants
    – public static final String INDEX = "atg-rest-index";
    – public static final String COUNT = "atg-rest-count";
    – public static final String TRANSIENT = "atg-rest-transient";
    – public static final String METHOD = "atg-rest-method";
    – public static final String OUTPUT = "atg-rest-output";
    – public static final String DEPTH = "atg-rest-depth";




                                     54
Rest Parameters - Output
 Allows the server’s configured output format to be overridden on
  each request
   – atg-rest-output=json
   – atg-rest-output=xml


 Map<String,Object> params = new HashMap<String,Object>();
  params.put(RestConstants.OUTPUT, “xml”);




                              55
Rest Parameters - Depth
 The atg-rest-depth parameter allows the caller to
 determine how deep of an object tree to return
   – Default is 0 which means references are returned as
     URLs
   – Value of 1 or more means that all references and
     multivalued objects will be included in the output




                           56
Rest Parameters - Depth
 <atgResponse>
  <displayName>My Product</displayName>
  <specs>
   http://localhost/rest/repository/atg/commerce/catalog
   /ProductCatalog/specs/spec12345
  </specs>
 </atgResponse>
 <atgResponse>
  <displayName>My Product</displayName>
  <specs>
   <color>red</color>
   <size>medium</size>
  </specs>
 </atgResponse>


                             57
Actionscript Client Lib
 Similar structure, classes, and methods as Java
 client lib
 Helper methods take resultHandler and
 faultHandler callback methods as arguments
 There is no RestResult class




                       58
Actionscript Client Lib
public function login():void {
    session = RestSession.createSession(getHostname(), getPort(), getUserName(), getPassword());
    session.useHttpsForLogin = false;
    session.login(handleLogin, handleLoginFault);
}


public function handleLogin(pEvent:Event):void {
    // handle login success
}


public function handleLoginFault(pEvent:Event):void {
    // handle login fault
}




                                                    59
Actionscript Client Lib
public function getItem():void {
        RestRepositoryHelper.getItem("/atg/commerce/catalog/ProductCatalog", "product",
        “prod12345”, null, session, handleResult, handleFault);
}
public function handleResult(pEvent:Event):void {
    if (session.outputFormat == RestConstants.JSON) {
        var json:Object = JSON.decode(pEvent.target.data);
        // do something with the data in json variable
    }
    else if (session.outputFormat == RestConstants.XML) {
        var xml:XML = new XML(pEvent.target.data);
        // do something with the data in xml variable
    }
}
public function handleFault(pEvent:Event):void {
    Alert.show("Fault: " + pEvent.toString());
}
                                                   60
Q&A
 Questions?




               61

						
Related docs
Other docs by kwd15566
Web Services at IRIS DMC
Views: 13  |  Downloads: 0
.NET XML Web Services
Views: 20  |  Downloads: 0
Define Web services technical specs
Views: 10  |  Downloads: 0
Seamless Integration of Web Services
Views: 6  |  Downloads: 0
Portal Web Services Support of DOE SciDAC
Views: 1  |  Downloads: 0