Creating Efficient Code Using ABAP 7.4 NW
February 5, 2017 | Author: Victor Hugo | Category: N/A
Short Description
Download Creating Efficient Code Using ABAP 7.4 NW...
Description
CD261
Creating Efficient Code Using the Renewed ABAP SAP NetWeaver 7.4 Holger Janz, Horst Keller
Disclaimer This presentation outlines our general product direction and should not be relied on in making a purchase decision. This presentation is not subject to your license agreement or any other agreement with SAP. SAP has no obligation to pursue any course of business outlined in this presentation or to develop or release any functionality mentioned in this presentation. This presentation and SAP's strategy and possible future developments are subject to change and may be changed by SAP at any time for any reason without notice. This document is provided without a warranty of any kind, either express or implied, including but not limited to, the implied warranties of merchantability, fitness for a particular purpose, or non-infringement. SAP assumes no responsibility for errors or omissions in this document, except if such damages were caused by SAP intentionally or grossly negligent.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
2
Agenda Previously on ABAP News – SAP NetWeaver 7.0 Enhancement Package 2 Expressions Inline Declarations Constructors Internal Table Expressions
Message Channels JSON Message and Push Channels
Further News Security Checks ABAP Doc Online Documentation © 2013 SAP AG or an SAP affiliate company. All rights reserved.
3
Previously on ABAP News Netweaver Release 7 EhP 2
Previously on ABAP News Expressions o->m( abs( i * 2 ) + 1 ).
Computation in Operands
IF o->m1( )->intf~m2( )->a > 1.
s2 = to_upper( condense( str ) ) && concat_lines_of( itab ).
s = |Hello, \n| & |today is { sy-datum date = iso } |now is { sy-uzeit time = iso }|.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
Method Call Chains
Expressions for String Operations
\n| &
String Templates
5
Previously on ABAP News Internal Tables and Numeric Types DATA itab TYPE HASHED TABLE OF s1 WITH UNIQUE KEY b WITH UNIQUE SORTED KEY k1 COMPONENTS a c. … READ TABLE itab WITH KEY k1 COMPONENTS a = 1 b = 2 ASSIGNING . LOOP AT itab INTO wa WHERE (cond). … ENDLOOP. DATA: binary_float TYPE f, decimal_fix TYPE p LENGTH 5 DECIMALS 2, decimal_float_small TYPE decfloat16, decimal_float_big TYPE decfloat34. © 2013 SAP AG or an SAP affiliate company. All rights reserved.
Secondary Keys
Dynamic WHERE Condition
Decimals Floating Point Type
6
Expressions
Expressions Motivation Declarative - What instead of How Functional - less side effects Modern – catch up with trends See: http://en.wikipedia.org/wiki/Declarative_programming http://en.wikipedia.org/wiki/Functional_programming
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
8
Expressions Inline Declarations Inline Declarations ... DATA(var) ... ... FIELD-SYMBOL() ... The new declaration operators DATA and FIELD-SYMBOL allow inline declarations of variables and field symbols with declaration expressions in declaration positions. Declaration positions are write positions where the operand type can be determined from the context statically.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
9
Expressions Inline Declarations – DATA( )
LOOP AT itab INTO DATA(wa). ... ENDLOOP.
oref->meth( IMPORTING p1 = DATA(a1) IMPORTING p2 = DATA(a2) ... ).
READ TABLE itab INTO DATA(wa) ...
FIND ... IN ... MATCH COUNT DATA(cnt).
DATA(ref) = class=>factory( ... ).
CALL TRANSFORMATION ... RESULT XML DATA(xml).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
10
Expressions Inline Declarations – DATA( )
DATA ixml TYPE REF TO if_ixml. DATA stream_factory TYPE REF TO if_ixml_stream_factory. DATA document TYPE REF TO if_ixml_document. ixml = cl_ixml=>create( ). stream_factory = ixml->create_stream_factory( ). document = ixml->create_document( ).
DATA(ixml) = cl_ixml=>create( ). DATA(stream_factory) = ixml->create_stream_factory( ). DATA(document) = ixml->create_document( ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
11
Expressions Inline Declarations – FIELD-SYMBOL( )
ASSIGN ... TO FIELD-SYMBOL().
LOOP AT itab ASSIGNING FIELD-SYMBOL(). ... ENDLOOP.
READ TABLE itab ASSIGNING FIELD-SYMBOL() ...
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
12
Expressions Inline Declarations – FIELD-SYMBOL( )
FIELD-SYMBOLS LIKE LINE OF itab. LOOP AT itab ASSIGNING . ... ENDLOOP.
LOOP AT itab ASSIGNING FIELD-SYMBOL(). ... ENDLOOP.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
13
Expressions Constructor Expressions Constructor Expressions ... NEW|VALUE|REF|EXACT|CONV|CAST|COND|SWITCH type( ... ) ... The new constructor operators
NEW creates objects VALUE creates values REF gets references EXACT performs lossless calculations or assignments CONV converts values CAST performs up or down casts COND and SWITCH enable conditional expressions
allow to construct results of a specified type in general expression positions.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
14
Expressions Constructor Expressions – NEW( ) FIELD-SYMBOLS TYPE data. DATA dref TYPE REF TO data. CREATE DATA dref TYPE i. ASSIGN dref->* TO . = 555.
DATA dref TYPE REF TO data. dref = NEW i( 555 ).
DATA(oref) = NEW class( ... ). DATA oref TYPE REF TO class. CREATE OBJECT oref EXPORTING ... DATA oref TYPE REF TO class. oref = NEW #( ... ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
15
Expressions Constructor Expressions – VALUE( )
DATA itab DATA wa wa-col1 = APPEND wa wa-col1 = APPEND wa
TYPE t_itab. LIKE LINE OF itab. 1. wa-col2 = 2. TO itab. 3. wa-col2 = 4. TO itab.
method( itab ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
DATA(itab) = VALUE t_itab( ( col1 = 1 col2 = 2 ) ( col1 = 3 col2 = 4 ) ). method( itab ).
method( VALUE t_itab( ( col1 = 1 col2 = 2 ) ( col1 = 3 col2 = 4 ) ) ).
16
Expressions Constructor Expressions – REF( ) DATA dref TYPE REF TO string. GET REFERENCE OF para INTO dref. DATA(ptab) = VALUE abap_parmbind_tab( ( name = name kind = cl_abap_objectdescr=>exporting value = dref ) ). CALL METHOD (class)=>(meth) PARAMETER-TABLE ptab. DATA(ptab) = VALUE abap_parmbind_tab( ( name = name kind = cl_abap_objectdescr=>exporting value = REF #( para ) ) ). CALL METHOD (class)=>(meth) PARAMETER-TABLE ptab. © 2013 SAP AG or an SAP affiliate company. All rights reserved.
17
Expressions Constructor Expressions – EXACT( )
TYPES numtext TYPE n LENGTH 255. DATA number TYPE numtext. TRY. MOVE EXACT '4 Apples + 3 Oranges' TO number. CATCH cx_sy_conversion_error INTO DATA(exc). ... ENDTRY. TYPES numtext TYPE n LENGTH 255. TRY. DATA(number) = EXACT numtext( '4 Apples + 3 Oranges' ). CATCH cx_sy_conversion_error INTO DATA(exc). ... ENDTRY.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
18
Expressions Constructor Expressions – CONV( ) DATA text TYPE c LENGTH 255. DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV #( text ) ).
DATA text TYPE c LENGTH 255. DATA helper TYPE string. helper = text. DATA(xstr) = cl_abap_codepage=>convert_to( source = helper ).
IF 1 / 3 > 0. ... ENDIF.
IF CONV decfloat34( 1 / 3 ) > 0. ... ENDIF.
IF ' ' = ` `. ... ENDIF.
IF ' ' = CONV char1( ` ` ). ... ENDIF.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
19
Expressions Constructor Expressions – CAST( )
DATA structdescr TYPE REF TO cl_abap_structdescr. structdescr ?= cl_abap_typedescr=>describe_by_name( 'T100' ). DATA(components) = structdescr->components.
DATA(components) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( 'T100' ) )->components.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
20
Expressions Constructor Expressions – COND( ) DATA time TYPE string. IF sy-timlo < '120000'. time = |{ sy-timlo TIME = ISO } AM|. ELSEIF sy-timlo > '120000'. time = |{ CONV t( sy-timlo - 12 * 3600 ) TIME = ISO } PM|. DATA(time) = ELSEIF sy-timlo = '120000'. COND string( time = |High Noon|. WHEN sy-timlo < '120000' THEN ELSE. |{ sy-timlo TIME = ISO } AM| RAISE EXCEPTION TYPE cx_cant_be. WHEN sy-timlo > '120000' THEN ENDIF. |{ CONV t( sy-timlo - 12 * 3600 ) TIME = ISO } PM| WHEN sy-timlo = '120000' THEN |High Noon| ELSE THROW cx_cant_be( ) ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
21
Expressions Constructor Expressions – SWITCH( ) DATA number TYPE string. CASE sy-index. WHEN 1. number = 'one'. WHEN 2. number = 'two'. WHEN 3. number = 'three'. WHEN OTHERS. RAISE EXCEPTION TYPE cx_overflow. ENDCASE.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
DATA(number) = SWITCH string( sy-index WHEN 1 THEN 'one' WHEN 2 THEN 'two' WHEN 3 THEN 'three' ELSE THROW cx_overflow( ) ). 22
Expressions Table Expressions Table Expressions ... itab[ ... ] ... The new table expressions itab[ … ] enable read access to internal tables at operand positions. The operand positions can be read positions and also some write positions
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
23
Expressions Table Expressions – table line wa = itab[ idx ]. READ TABLE itab INDEX idx INTO wa. READ TABLE itab INDEX idx USING KEY key INTO wa.
READ TABLE flights WITH KEY col1 = ... col2 = ... INTO wa.
wa = itab[ KEY key INDEX idx ].
wa = itab[col1 = ... col2 = ... ].
wa = itab[ KEY key COMPONENTS col1 = ... col2 = ... ].
READ TABLE flights WITH TABLE KEY key COMPONENTS col1 = ... col2 = ... INTO wa.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
wa = itab[ KEY key col1 = ... col2 = ... ]. 24
Expressions Table Expressions – result ... itab[ ... ] ...
READ TABLE ... ASSIGNING ...
Performance considerations! ... VALUE type( itab[ ... ] ) ...
... REF type( itab[ ... ] ) ...
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
READ TABLE ... INTO ...
READ TABLE ... REFERENCE INTO ...
25
Expressions Table Expressions – chaining
2
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
1
READ TABLE itab INTO DATA(wa1) INDEX 2. READ TABLE wa1-col2 INTO DATA(wa2) INDEX 1. READ TABLE wa2 INTO DATA(wa3) INDEX 2. DATA(num) = wa3-col1.
10
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
DATA(num) = itab[ 2 ]-col2[ 1 ][ 2 ]-col1.
26
Expressions Internal Tables Built-in Functions
READ TABLE itab WITH ... TRANSPORTING NO FIELDS. DATA(idx) = sy-tabix.
DATA(idx) = line_index( itab[ ... ] ).
READ TABLE itab WITH ... TRANSPORTING NO FIELDS. IF sy-subrc = 0. ... ENDIF.
IF line_exists( itab[ ... ] ). ... ENDIF.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
27
Expressions ABAP is Extensively Expression Enabled Skip Old Fashioned Style!
MOVE source TO target.
COMPUTE result = anything.
CALL METHOD meth EXPORTING ... RECEIVING ...
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
LeftHandSide = RightHandSide.
28
Expressions ABAP is Extensively Expression Enabled – Example I DATA: itab TYPE tt, wa LIKE LINE OF itab. FIELD-SYMBOLS: TYPE ts, TYPE ts.
DATA(itab) = VALUE tt( ( c1 = 7 c2 = 9 ) ( c1 = 3 c2 = 5 ) ). itab[ 1 ]-c2 = itab[
c2 = 5
]-c1.
wa-c1 = 7. wa-c2 = 9. INSERT wa INTO TABLE itab. wa-c1 = 3. wa-c2 = 5. INERT wa INTO TABLE itab. READ TABLE itab INDEX 1 ASSIGNING . READ TABLE itab WITH KEY c2 = 5 ASSIGNING . -c2 = -c1.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
29
Expressions ABAP is Extensively Expression Enabled – Example II
CONSTANTS id TYPE i VALUE 17.
DATA(factory) = NEW cl_factory( ). factory->service( CONV #( id ) ).
… DATA: factory TYPE REF TO cl_factory, id_string TYPE string. CREATE OBJECT factory.
Even
id_string = id. factory->service( id_string ). NEW cl_factory( )->service( CONV #( id ) ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
30
Expressions ABAP is Extensively Expression Enabled – Pitfalls
Obfuscating lcl=>cm_ii( lcl=>factory2( f_in1 = lcl=>factory0( )->im_ii( i ) f_in2 = lcl=>cm_ii( 2 ** i ) - 3 + itab[ 2 ]-s-xtab[ x = 'abc' ] )->m_ii( lcl=>factory1( f_in = itab[ a = 2 b = 4711 / 2 ]-x )->m_lif_i( lcl=>cm_ii( 5 ) )->im_ii( 6 ) ) ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
Performance itab1[ 1 ]-a = itab2[ 1 ]-x. itab1[ 1 ]-b = itab2[ 1 ]-y. itab1[ 1 ]-c = itab2[ 1 ]-z.
31
Exercise - Expressions
Message Channels
Message Channels Motivation Event-driven – no polling Full-Duplex – communication in both directions Standard – integration with different UI technologies
See: http://en.wikipedia.org/wiki/WebSocket http://www.w3.org/TR/websockets
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
34
JSON ABAP and JSON JSON Support in sXML-Library
Backported to 7.02!
JSON - JavaScript Object Notation, data format in text form for data exchange. JSON-XML - SAP-specific representation of JSON data in XML format asJSON - ABAP Serialization JSON, Canonical JSON representation for serialization/deserialization of ABAP data by transformation ID
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
35
JSON Readers and Writers JSON to JSON-XML and Vice Versa DATA(json) = cl_abap_codepage=>convert_to( `{"TEXT":"JSON"}` ). DATA(json_reader) = cl_sxml_string_reader=>create( json ). DATA(xml_writer) = cl_sxml_string_writer=>create( ). json_reader->next_node( ). json_reader->skip_node( xml_writer ). cl_demo_output=>display_xml( xml_writer->get_output( ) ).
DATA(xml) = cl_abap_codepage=>convert_to( `JSON` ). DATA(xml_reader) = cl_sxml_string_reader=>create( xml ). DATA(json_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). xml_reader->next_node( ). xml_reader->skip_node( json_writer ). cl_demo_output=>display_json( json_writer->get_output( ) ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
36
JSON Transformation ID JSON to JSON-XML and Vice Versa DATA(json) = `{"TEXT":"JSON"}`. CALL TRANSFORMATION id SOURCE XML json RESULT XML DATA(xml). cl_demo_output=>display_xml( xml ).
DATA(xml) = `JSON`. DATA(json_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). CALL TRANSFORMATION id SOURCE XML xml RESULT XML json_writer. cl_demo_output=>display_json( json_writer->get_output( ) ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
37
JSON asJSON ABAP to JSON and Vice Versa DATA(text) = `JSON`. DATA(json_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). CALL TRANSFORMATION id SOURCE text = text RESULT XML json_writer. cl_demo_output=>display_json( json_writer->get_output( ) ).
DATA(json) = `{"TEXT":"JSON"}`. DATA text TYPE string. CALL TRANSFORMATION id SOURCE XML json RESULT text = text. cl_demo_output=>display( text ).
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
38
ABAP Channels ABAP Messaging Channels (AMC) and ABAP Push Channels (APC)
A P C AS ABAP Session Application Server 1
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
A P C AMC
WebSocket Protocol
AS ABAP Session Application Server 2
39
ABAP Channels Goal Support of Interactive and collaborative scenarios based on event-driven UIs and “real-time” web Require exchange of messages between user sessions and user agents residing on different servers
Scope: Push channel for full-duplex communication with user agents via WebSockets in ABAP Publish/subscribe infrastructure for exchange of messages between user sessions and user agents residing on different servers via ABAP Messaging Channels
Session A
Session B
Session C
ABAP App Server X
ABAP App Server Y
SAP System © 2013 SAP AG or an SAP affiliate company. All rights reserved.
SAP CONFIDENTIAL & INTERNAL
40
ABAP Channels Characteristcs ABAP Push Channel (APC) Integration of WebSocket (RFC 6455) into ABAP Outlook: SAPGUI Push Channel
ABAP Messaging Channel (AMC) Inter-Session Messaging (Pub/Sub) in ABAP Binding AMC channels to ABAP sessions
Collaboration (AMC & APC) Binding AMC channels to APC (WebSocket) Connections
Goal: Replace polling by event-driven approach whenever possible
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
41
ABAP Channels ABAP Messaging Channels Enable message based communications between ABAP programs across the boundaries of application servers. CAST if_amc_message_producer_text( cl_amc_channel_manager=>create_message_producer( i_application_id = 'DEMO_AMC' i_channel_id = '/demo_text' ) )->send( i_message = ... ). cl_amc_channel_manager=>create_message_consumer( i_application_id = 'DEMO_AMC' i_channel_id = '/demo_text' )->start_message_delivery( i_receiver = receiver ). WAIT FOR MESSAGING CHANNELS UNTIL receiver->text_message IS NOT INITIAL UP TO 60 SECONDS. © 2013 SAP AG or an SAP affiliate company. All rights reserved.
42
ABAP Channels ABAP Push Channels – ABAP side Enable bidirectional communications between ABAP programs and the internet using the WebSocket protocol. APCs can be connected to AMCs.
METHOD if_apc_ws_extension~on_start. ... IF amc_flag = abap_true. TRY. i_context->get_binding_manager( )->bind_amc_message_consumer( i_application_id = 'DEMO_AMC' i_channel_id = '/demo_text' ). CATCH cx_apc_error INTO DATA(exc). MESSAGE exc->get_text( ) TYPE 'X'. ENDTRY. ELSE. "Default behavior ENDIF. ENDMETHOD.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
METHOD if_apc_ws_extension~on_message. ... IF amc_flag = abap_true. CAST if_amc_message_producer_text( cl_amc_channel_manager=>create_message_producer( i_application_id = 'DEMO_AMC' i_channel_id = '/demo_text' ) )->send( i_message = ... ). ELSE. DATA(message_manager) = i_message->get_context( )->get_message_manager( ). DATA(message) = message_manager->create_message( ). message->set_text( ... ). message_manager->send( message ). ENDIF.
43
ABAP Channels ABAP Push Channels – Internet side
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
44
ABAP Channels Example: Chat Exchanging of messages between user agents running in Chrome, Firefox, SAPGUI and under different user sessions Chrome
Firefox
User A
User B
SAPGUI
SAPGUI
User C
User D
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
SAP CONFIDENTIAL & INTERNAL
45
Demo Exercise – Message Channels
Further News
Further News Security Checks “SLIN_SEC” DATA name TYPE string. DATA customers TYPE TABLE OF scustom WITH EMPTY KEY. cl_demo_input=>request( CHANGING field = name ). DATA(cond) = `country = 'DE' AND name = '` && name && `'`. TRY. SELECT * FROM scustom INTO TABLE customers WHERE (cond). cl_demo_output=>display( customers ). CATCH cx_sy_dynamic_osql_syntax. cl_demo_output=>display( 'Wrong input' ). ENDTRY.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
48
Further News ABAP Doc for ABAP in Eclipse Inline Documentation of Source Code based Development Objects
F2
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
49
Documentation See ABAP Keyword Documentation
Classic © 2013 SAP AG or an SAP affiliate company. All rights reserved.
ADT 50
Documentation ABAP Keyword Documentation in Eclipse Permanent Input Field, Executable Examples
F1
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
51
Further Information
SAP Public Web scn.sap.com/community/abap/blog/2013/07/22/abap-news-for-release-740 - ABAP 7.4 News Blog scn.sap.com/community/abap - ABAP Community help.sap.com/abapdocu_740/en - ABAP Online Documentation/Reference 7.4 scn.sap.com/docs/DOC-41566 - SCN Trail Editions: SAP NetWeaver Application Server 7.4
SAP Education and Certification Opportunities www.sap.com/education
Watch SAP TechEd Online www.sapteched.com/online
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
52
SAP TechEd Virtual Hands-on Workshops and SAP TechEd Online Continue your SAP TechEd education after the event! SAP TechEd Virtual Hands-on Workshops
SAP TechEd Online
Access hands-on workshops post-event Available January – March 2014 Complementary with your SAP TechEd registration
Access replays of keynotes, Demo Jam, SAP TechEd LIVE interviews, select lecture sessions, and more! View content only available online
http://saptechedhandson.sap.com/
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
http://sapteched.com/online
53
Feedback Please complete your session evaluation for CD261.
Thanks for attending this SAP TechEd session.
© 2013 SAP AG or an SAP affiliate company. All rights reserved. No part of this publication may be reproduced or transmitted in any form or for any purpose without the express permission of SAP AG. The information contained herein may be changed without prior notice. Some software products marketed by SAP AG and its distributors contain proprietary software components of other software vendors. National product specifications may vary. These materials are provided by SAP AG and its affiliated companies ("SAP Group") for informational purposes only, without representation or warranty of any kind, and SAP Group shall not be liable for errors or omissions with respect to the materials. The only warranties for SAP Group products and services are those that are set forth in the express warranty statements accompanying such products and services, if any. Nothing herein should be construed as constituting an additional warranty. SAP and other SAP products and services mentioned herein as well as their respective logos are trademarks or registered trademarks of SAP AG in Germany and other countries. Please see http://www.sap.com/corporate-en/legal/copyright/index.epx#trademark for additional trademark information and notices.
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
55
View more...
Comments