Skip to Content

Executive summary

Which approach to choose for bringing out OData services to expose entities?

  1. Do the Read and Query operations need coding that cannot be expressed in a CDS view, for example because BAPI (function module) calls are needed?
    1. Yes: Use the (traditional) code-based implementation approach, you cannot use SADL.
      1. Is the data volume per request small?
        1. Yes: Map OData operations to RFC calls.
          This has the advantage of simplifying, or eliminating the need for, middleware code.
        2. No: Implement operations by redefining methods in _DPC_EXT
  2. Read and Query operations can be expressed as CDS views.
    Is the data stored in existing tables?

    1. Yes: Is the data updated via existing APIs? (e.g. BAPIs?)
      1. Yes: Use a referenced or mapped data source, implement updates in _DPC_EXT
      2. No: Are the entities of the CDS view data source to be added to an existing OData service?
        1. Yes: Use a referenced or mapped data source
        2. No: Use CDS-BOPF-OData service exposure with @OData.publish: true
  3. The data is not stored in existing tables. New tables are to be created (SE11).
    Are the entities of the CDS view data source to be added to an existing OData service?

    1. Yes: Use a referenced or mapped data source
    2. No: Use CDS-BOPF-OData service exposure with @OData.publish: true
  4. Are you to use a referenced or mapped data source?
    1. Yes:
      Are there pre-existing properties, associations and actions (i.e. function imports) to be mapped to the data source?
      Or are there navigation properties to be defined between CDS- and non-CDS-based entities?
      Or is there a need to control the mapping of the properties, associations and actions to the referenced data source, e.g. set property EDM types?

      1. Yes: Map the implementation of the OData Service to a SADL model using the mapping editor in SEGW
      2. No: Use a referenced CDS view data source

The page Generating an OData Service Based on a CDS View also contains helpful use – and don’t use – cases for choosing between the available OData exposure options.

Author and motivation

Laszlo Kajan is a full stack Fiori/SAPUI5 expert, present on the SAPUI5 field since 2015.

The motivation behind this blog post is to provide a tutorial for back-end developers working in Fiori/SAPUI5 development teams wishing to move forward from pure _EXT-class-method-redefinition-based OData service implementation to the ‘ABAP Programming Model for SAP Fiori‘, including CDS views and BOPF. Guidance for choosing the right implementation method – extracted from blog posts and documentation – is provided in the ‘Executive summary’.

Part 1 – OData service with existing tables, DDIC data source, referenced CDS view data source and custom action

Let’s assume that you have an existing custom OData service, which is to be extended with a referenced CDS view data source, and a custom action (i.e. function import). All tables already exist.

DDIC data source

Create the ‘existing’ OData service:

  1. SEGW, create new project ‘Zx_SADL_PART_1’, type ‘Service with SAP Annotations’
  2. Add entity type ‘Customers’
    1. Right click ‘Data Model’ / Import / DDIC Structure
      1. Name = Customers
      2. ABAP Structure = SCUSTOM
      3. Select properties = [ID, NAME, CITY, EMAIL]
      4. Key = ID
    2. Optional: change type of property ‘Id’ to ‘Edm.Decimal’ with precision = 8 and ignore the warning/error
  3. Map implementation of CustomerSet to DDIC data source ‘SCUSTOM’
    1. Right click service implementation / CustomerSet / Map to Data Source / Type = Business Entity / Launch value help on ‘Name’ (F4)
    2. SADL Model Type = ‘DDIC’, SADL Model = ‘SCUSTOM’
  4. Generate, register and test the OData service
    1. /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/CustomersSet?$filter=Email eq ’’&$orderby=Id desc&$top=20
    2. Optional: try to update or create an entity, and notice how the response is ‘The requested service is not supported by entity ~SCUSTOM’

CDS interface view

Extend the existing OData service with a referenced CDS view data source.

Create the base CDS interface view:

  1. Launch Eclipse on perspective ABAP, connect to your back-end system
  2. Create interface (aka. base) view for carrier (i.e. airline) data
    1. Right click project / New / Other… / Data Definition
    2. Name = ‘Zx_I_SADL_PART_1_CARR’, Description = ‘CARR interface view for Zx_SADL’
      1. Note: ‘_I_’ is for interface view
    3. Template = ‘Define View’
    4. Change to:
      @AbapCatalog.sqlViewName: 'zx_i_carr1'
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'CARR interface view for Zx_SADL'
      @VDM.viewType: #BASIC
      define view Zx_I_SADL_PART_1_CARR
        as select from scarr as ca
      {
        key ca.carrid,
            ca.carrname,
            ca.currcode,
            ca.url
      }
    5. Activate
  3. Create interface view for flight data
    1. New Data Definition, Name = ‘Zx_I_SADL_PART_1_FLI’, Description = ‘FLIGHT interface view for Zx_SADL’, template = ‘Define View’
    2. Change to:
      @AbapCatalog.sqlViewName: 'zx_i_fli1'
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'FLIGHT interface view for Zx_SADL'
      @VDM.viewType: #BASIC
      define view ZX_I_SADL_PART_1_FLI
        as select from sflight as fl
      {
        key fl.carrid,
        key fl.connid,
        key fl.fldate,
            fl.planetype,
            fl.price,
            fl.currency,
            fl.seatsmax
      }​
    3. Activate
  4. Create associations between carriers and flights
    1. Change ‘Zx_I_SADL_PART_1_CARR’ to:
      define view Zx_I_SADL_PART_1_CARR
        as select from scarr as ca
        association [0..*] to Zx_I_SADL_PART_1_FLI as _fl on $projection.carrid = _fl.carrid
      {
        key ca.carrid,
            ca.carrname,
            ca.url,
            /* Associations */
            _fl
      }​
    2. Change ‘Zx_I_SADL_PART_1_FLI’ to:
      as select from sflight as fl
        association [1..1] to Zx_I_SADL_PART_1_CARR as _ca on $projection.carrid = _ca.carrid
      {
      […]
            fl.seatsmax,
            /* Associations */
            _ca
      }​
    3. Visualize the entities
      1. Right click DDL data definition / Open With / Graphical Editor

Business object annotations (BOPF)

Add business object (BO) @ObjectModel annotations for BOPF (Adding Business Object Semantics to Data Model):

  1. Change ‘Zx_I_SADL_PART_1_CARR’ to:
    @EndUserText.label: 'CARR interface view for Zx_SADL'
    @ObjectModel: {
        compositionRoot: true,
        modelCategory: #BUSINESS_OBJECT,
        representativeKey: 'carrid',
        semanticKey: [ 'carrid' ],
        transactionalProcessingEnabled: true,
        writeActivePersistence: 'scarr',
        
        createEnabled: false,
        updateEnabled: true,
        deleteEnabled: false
    }
    […]
          ca.url,
          /* Associations */
          @ObjectModel.association.type:  [#TO_COMPOSITION_CHILD]
          _fl
    }​
  2. Change ‘Zx_I_SADL_PART_1_FLI’ to:
    @EndUserText.label: 'FLIGHT interface view for Zx_SADL'
    @ObjectModel: {
        representativeKey: 'fldate',
        semanticKey: [ 'carrid', 'connid', 'fldate' ],
        writeActivePersistence: 'sflight',
    
        createEnabled: true,
        deleteEnabled: true,
        updateEnabled: true
    }
    […]
    $projection.carrid = _ca.carrid
    {
          @ObjectModel.readOnly: true
          @ObjectModel.foreignKey.association: '_ca'
      key fl.carrid,
    […]
          /* Associations */
          @ObjectModel.association.type: [#TO_COMPOSITION_ROOT, #TO_COMPOSITION_PARENT]
          _ca
    }​
  3. Activate both views in one step
  4. Observe the generated artifacts
    1. SAP GUI / BOBX
      1. Find and open new local business object ‘Zx_I_SADL_PART_1_CARR’
      2. Observe the node structure and the actions of the nodes
      3. Test the business object: F8
        1. Load node instance by alternative key ‘*_CARR – DB KEY’ = ‘AA’
        2. Navigate the CARR object to its flights through the association
    2. Test the CDS views, follow association
      1. Right click CDS view definition of Zx_I_SADL_PART_1_CARR in Eclipse / Open With / Data Preview
      2. Place cursor on ‘carrid’ = ‘AA’, click triangle after ‘Zx_I_SADL_PART_1_CARR’ in title to get a list of associations, navigate to ‘_fl’
      3. Observe the list of flights retrieved via the association

Custom business object action

Define a new action (i.e. function import) ‘ZTOGL_URL_SLASH’:

  1. BOBX, open Zx_I_SADL_PART_1_CARR, enter ‘Edit’ mode
  2. New action on Zx_I_SADL_PART_1_CARR
    1. Action Name = ‘ZTOGL_URL_SLASH’
    2. Description = ‘Toggle slash at end of URL’
    3. Action cardinality = ‘Single Node Instance’
    4. Implementing Class = ‘ZCL_x_I_SADL_PART_1’
    5. Exporting Parameter = ‘Node’
    6. Exporting Parameter BO = ‘Zx_I_SADL_PART_1_CARR’
    7. Exporting Parameter Node = ‘Zx_I_SADL_PART_1_CARR’
    8. Exporting Cardinality = ‘1’
    9. Save
    10. Double click implementing class, create it
    11. Activate the implementing class without changes, (re)generate the business object
  3. Implement action ‘ZTOGL_URL_SLASH’
    1. Edit ‘ZCL_x_I_SADL_PART_1’ (in SE80 / Eclipse)
    2. Change superclass to ‘/BOBF/CL_LIB_A_SUPERCL_SIMPLE’, don’t keep redefinitions
    3. Description = ‘Action class for BO node Zx_I_SADL_PART_1_CARR’
    4. Look up the ‘Constants Interface’ of node ‘Zx_I_SADL_PART_1_CARR’ in BOBX: ‘ZIF_x_I_SADL_PART_1_CAR_C’
    5. Redefine ‘/BOBF/IF_FRW_ACTION~EXECUTE’:
          CASE is_ctx-act_key.
            WHEN zif_x_i_sadl_part_1_car_c=>sc_action-zx_i_sadl_part_1_carr-ztogl_url_slash.
              " Declare output table of Action
              DATA lt_carr TYPE ztx_i_sadl_part_1_carr.
      
              " Read input carrier instance
              io_read->retrieve(
                EXPORTING
                  iv_node                 = is_ctx-node_key " Node Name
                  it_key                  = it_key          " Key Table
                IMPORTING
                  et_data                 = lt_carr         " Data Return Structure
              ).
      
              " Assuming single instance for the action
              READ TABLE lt_carr ASSIGNING FIELD-SYMBOL(<fs_carr>) INDEX 1.
              IF sy-subrc = 0.
                " Make changes to the business object:
                "   * Set new URL
                IF substring( val = <fs_carr>-url off = numofchar( <fs_carr>-url ) - 1 len = 1 ) EQ '/'.
                  <fs_carr>-url = substring( val = <fs_carr>-url off = 0 len = numofchar( <fs_carr>-url ) - 1 ).
                ELSE.
                  <fs_carr>-url = |{ <fs_carr>-url }/|.
                ENDIF.
      
                " Now update the BO instance
                io_modify->update(
                  EXPORTING
                    iv_node           = is_ctx-node_key             " Node
                    iv_key            = <fs_carr>-key               " Key
                    iv_root_key       = <fs_carr>-root_key          " NodeID
                    is_data           = REF #( <fs_carr>-node_data )" Data
                    it_changed_fields = VALUE #( ( zif_x_i_sadl_part_1_car_c=>sc_node_attribute-zx_i_sadl_part_1_carr-url ) ) ).
      
                et_data = lt_carr.
              ENDIF.
            WHEN OTHERS.
          ENDCASE.​
    6. Activate
  4. Test action ‘ZTOGL_URL_SLASH’:
    1. BOBX / Test (F8) / Load node instance by alternative key ‘*_CARR – DB KEY’ = ‘AA’
    2. Select CARRID ‘AA’, Execute Action ‘ZTOGL_URL_SLASH’
    3. Observe how a trailing slash ‘/’ appears and disappears
    4. You can save, or discard the changes made in the test suite

CDS consumption view

Create the CDS consumption view that is to be consumed by the referenced CDS view data source:

  1. Create carrier (airline) consumption view
    1. Eclipse / New Data Definition: Name = ‘Zx_C_SADL_PART_1_CARR’, Description = ‘CARRIER consumption view for Zx_SADL’, template = ‘Define View’
      1. Note: ‘_C_’ is for consumption view
    2. Change to:
      @AbapCatalog.sqlViewName: 'zx_c_carr1'
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'CARRIER consumption view for Zx_SADL'
      @ObjectModel: {
          compositionRoot: true,
          transactionalProcessingDelegated: true
      
          updateEnabled: true
      }
      @UI: {
          headerInfo: { typeNamePlural: 'Airlines' }
      }
      @VDM.viewType: #CONSUMPTION
      define view Zx_C_SADL_PART_1_CARR
        as select from Zx_I_SADL_PART_1_CARR as ca
        association [0..*] to Zx_C_SADL_PART_1_FLI as _fl on $projection.carrid = _fl.carrid
      {
        key ca.carrid,
            @EndUserText.label: 'Airline Name'
            ca.carrname as CarrierName,
            @Semantics.url: true
            ca.url,
            /* Associations */
            _fl
      }​
      1. Note how field aliases (‘as’), @Semantics and @UI annotations are placed into the consumption view
      2. Note how e.g. ‘updateEnabled’ is repeated. These annotations affect both the SADL and the BOPF level. Annotations on the interface view enforce the requested behavior. Annotations on the consumption view control the entity set metadata. Always keep these annotations in sync.
  2. Create flight consumption view
    1. Eclipse / New Data Definition: Name = ‘Zx_C_SADL_PART_1_FLI’, Description = ‘FLIGHT consumption view for Zx_SADL’, template = ‘Define View’
    2. Change to:
      @AbapCatalog.sqlViewName: 'zx_c_fli1'
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'FLIGHT consumption view for Zx_SADL'
      @ObjectModel: {
          createEnabled: true,
          updateEnabled: true,
          deleteEnabled: true
      }
      @VDM.viewType: #CONSUMPTION
      define view Zx_C_SADL_PART_1_FLI
        as select from Zx_I_SADL_PART_1_FLI as fl
        association [1..1] to Zx_C_SADL_PART_1_CARR as _ca on $projection.carrid = _ca.carrid
      {
        key fl.carrid,
        key fl.connid,
        key fl.fldate,
            @EndUserText.label: 'Airplante Type'
            fl.planetype,
            @Semantics.amount.currencyCode: 'CurrencyCode'
            fl.price,
            @Semantics.currencyCode: true
            fl.currency as CurrencyCode,
            fl.seatsmax,
            /* Association */
            _ca
      }​
  3. Activate both views at the same time
  4. Test the consumption views in Eclipse with data preview

Referenced CDS consumption view data source

Extend the existing OData service by referencing the new CDS consumption view:

  1. SEGW / open project ‘Zx_SADL_PART_1’
  2. Right click ‘Data Model’ / ‘Reference’ / ‘Data Source’ / CDS-Entity = ‘Zx_C_SADL_PART_1_CARR’ / Next
    1. Check in all the check boxes in ‘Selected’ column, Finish
  3. Observe and explore the new ‘Data Source References’ node
    1. Note how ‘…FLIType.connid’ is type ‘Edm.String’, instead of the best-practices ‘Edm.Decimal’
    2. Check out the permitted operations of the ‘Entity Sets’
    3. Note how the BO action ‘ZTOGL_URL_SLASH’ is exposed as a ‘Function Import’, even though this is not requested anywhere in the CDS
  4. Save and generate the project

Test the OData service. Launch /IWFND/GW_CLIENT or a REST client e.g. Postman:

  1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/$metadata?sap-ds-debug=true
    1. Postman: retrieve the CSRF token with:
      1. Header ‘x-csrf-token’ = ‘Fetch’
  2. Get carrier ‘AA’
    1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR?$filter=carrid eq ‘AA’
      1. Use header ‘Accept’ = ‘application/json’
  3. Follow navigation properties
    1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR(‘AA’)/to_fl?$top=3
    2. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_FLI(carrid=’AA’,connid=’0017′,fldate=datetime’2016-08-17T00%3A00%3A00′)/to_ca
  4. Note down the function import name, e.g. ‘ACC0323FA175F5649EE81C514Ztogl_url_slash’
  5. Call function import
    1. POST /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/…Ztogl_url_slash?carrid=’AA’
    2. Get carrier ‘AA’ again
  6. Update ‘url’ of carrier ‘AA’ (using PATCH or MERGE if available)
    1. PATCH /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR(‘AA’)
      1. Header ‘Content-Type’ = ‘application/json’
      2. Body: copy from GET above, change the URL, e.g.:
        {
            "carrid": "AA",
            "CarrierName": "American Airlines",
            "url": "http://www.aa.com/"
        }​
      3. Status 204 ‘No Content’ indicates success
  7. Attempt to create new airline ‘ZZ’
    1. POST /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR
      1. Header ‘Content-Type’ = ‘application/json’
      2. Body: copy from GET above, change the URL, e.g.:
        {
            "carrid": "ZZ",
            "CarrierName": "Zambian Zoom",
            "url": "http://www.zz.com"
        }​
      3. The request fails with status 500 with message ‘The ASSERT condition was violated’: create is not allowed on the BOPF level.
        1. Note: in case e.g. create is allowed on the BOPF level (interface view) but isn’t allowed on the OData metadata level (consumption view), entity creation will succeed.

Part 2 – CDS-BOPF-OData service exposure with @OData.publish: true

In this solution no SEGW gateway project is used. The CDS view is edited in Eclipse.

  1. Eclipse / ABAP perspective / Right click project / New / Other… / Data Definition
    1. Create (or reuse) interface CDS views ‘Zx_I_SADL_PART_1_CARR’ and ‘Zx_I_SADL_PART_1_FLI’ from the section above
    2. Add (or reuse) custom action ‘ZTOGL_URL_SLASH’ to BO ‘Zx_I_SADL_PART_1_CARR’ from the section above
    3. Create new consumption views ‘Zx_C_SADL_PART_2_CARR’ and ‘Zx_C_SADL_PART_1_FLI’ as below:
      1. ‘Zx_C_SADL_PART_2_CARR’: description ‘CARRIER consumption view for @OData.publish: true’
        1. Create it like ‘Zx_C_SADL_PART_1_CARR’ above
        2. @AbapCatalog.sqlViewName: ‘zx_c_carr2’
        3. Association to ‘Zx_C_SADL_PART_2_FLI’
        4. Add view-level annotation ‘@OData.publish: true’
      2. ‘Zx_C_SADL_PART_2_FLI’: description ‘FLIGHT consumption view for @OData.publish: true’
        1. Create it like ‘Zx_C_SADL_PART_1_FLI’ above
        2. @AbapCatalog.sqlViewName: ‘zx_c_fli2’
        3. Association to ‘Zx_C_SADL_PART_2_CARR’
    4. Activate consumption views together
    5. Note the warning on the line with ‘@OData.publish: true’: “Service … is not active”
    6. Go to ‘/IWFND/MAINT_SERVICE’ and add service ‘Zx_C_SADL_PART_2_CARR’
      1. System alias = LOCAL

Test the OData service ‘/sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS’:

  1. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/$metadata?sap-ds-debug=true
    1. Postman: retrieve the CSRF token with:
      1. Header ‘x-csrf-token’ = ‘Fetch’
  2. Continue the test like for ‘Zx_SADL_PART_1_SRV’ above
    1. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR?$filter=carrid eq ‘AA’
    2. POST /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/A5393E1DDD898C21B108F25FCZtogl_url_slash?carrid=’AA’
    3. PATCH /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR(‘AA’)
    4. POST /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR
Summary

Following this tutorial you created an OData service with existing tables, DDIC data source, referenced CDS view data source and custom function import as a BO action. You also created an OData service using the ABAP Development Tools for Eclipse, CDS views and BOPF.

You now know how to use an interface (base), and a consumption view for OData exposure. You gained experience to make the right implementation choice for your next OData project, and you know where to find further information on the topic.

Further reading

 

Afterword

Thank you for going through this tutorial. I hope you found it useful. I hope it raised questions as well. Do follow the above, or other links to satisfy your curiosity – a sure way to deepen your understanding.

Another very interesting topic and a candidate for future posting is Christian Theilemann’s “Reactive state management in SAPUI5 via MobX“. I recommend you check it out. Update: “Adding Reactive State Management with Validation to Existing UI5 Application – a Tutorial” has now been published.

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Jelena Perfiljeva

    Can’t speak for the technical side as we’re not there yet but this looks really impressive! Clear structure and additional references are helpful to those who want to learn more. Nicely done!

     

    (1) 
  2. Sebastian Freilinger-Huber

    Hi Laszlo,

    thanks a lot for sharing your information and thoughts about the programming model.
    I appreciate colleagues, who take the time to share their thoughts in such a detailed manner plus adding further readable content. As you hoped in the end, that some questions will arise, I will take the chance to provide a few as noone did before. Just to make sure, they should not be understood as some sort of criticism, but should be maybe work as the starting point of a discussion.

    I personally use this model also for two years now productivly, mainly based on Netweaver 7.50 and even some early tries with 7.40…Considering a few points in your tutorial, it would be very nice if you could share some deeper thoughts:

    • Unfortunately I am missing the information, on which system state your tutorial is based on? As the programming model evolved coming from 7.40 until 7.52 nowadays (and very soon 7.53) the best practises what to achieve how are highly dependent on the Netweaver Release on my opinion. However based on the annotations you use, I assume we are talking about >= 7.50.
    • In your Executive Summary you reach several times the auto-exposure scenario, when “the entities are not to be added to an existing OData Service”. So if I understand you correctly, when you create a new one you would use auto-exposure. Here I am a little confused, because from my understanding/experience using RDS (reference data source) should be the only solution for >= 7.50 (although some documentation might tell you something else), when you define your service based on a CDS Consumption View. I would be interessted in your thoughts why using auto exposure at all? On my opinion the implementation overhead of defining the RDS in very low and the benefit of the possibility to redefine the data and model providers is huge. Maybe I am missing some Auto-Exposure advantages? To me this is only usable in exercises or prototypes (where quick and dirty might be applicable) but not productivly in the real world… For 7.40 as RDS and Auto-Exposure is not available, obviously mapped data source should be the only way to go (not sure about scenarios before 7.40, where CDS was not available at all).
    • When you create an Action on your generated Business Object, you use transaction BOBX. I am wondering why you do not use Eclipse to accomplish this. Based on my experience with Netweaver >= 7.50 Eclipse should be the tool to be used (at least if you are on SP 5 or something in this area) as new functionality will find his way to Eclipse only. BOBX should not be used anymore (especially for newly created/generated BOs), if you implement a new BO in >= 7.50.

    Looking forward to your (or other) opinions about the topic.

    Best regards,

    Sebastian

    (0) 

Leave a Reply