From 4b497060a0da0c36b1c6de462724fe566eefc344 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 10 Apr 2014 15:28:19 +0200 Subject: [PATCH] Added an example to embed EWF nino service into a Vision2 desktop application. This is locally consumed via the embedded web browser component. --- examples/desktop_app/README.md | 2 + examples/desktop_app/desktop_app.ecf | 28 +++ examples/desktop_app/files/index.html | 1 + examples/desktop_app/home.html | 32 +++ .../src/app_embedded_web_service.e | 230 ++++++++++++++++++ examples/desktop_app/src/desktop_app.e | 71 ++++++ examples/desktop_app/src/main_window.e | 202 +++++++++++++++ .../src/service/embedded_web_service.e | 114 +++++++++ .../shared_embeded_web_service_information.e | 27 ++ 9 files changed, 707 insertions(+) create mode 100644 examples/desktop_app/README.md create mode 100644 examples/desktop_app/desktop_app.ecf create mode 100644 examples/desktop_app/files/index.html create mode 100644 examples/desktop_app/home.html create mode 100644 examples/desktop_app/src/app_embedded_web_service.e create mode 100644 examples/desktop_app/src/desktop_app.e create mode 100644 examples/desktop_app/src/main_window.e create mode 100644 examples/desktop_app/src/service/embedded_web_service.e create mode 100644 examples/desktop_app/src/service/shared_embeded_web_service_information.e diff --git a/examples/desktop_app/README.md b/examples/desktop_app/README.md new file mode 100644 index 00000000..f9054fb4 --- /dev/null +++ b/examples/desktop_app/README.md @@ -0,0 +1,2 @@ +This example demonstrates the use of embedded Vision2 web browser component, and embedded EWF server (using nino). + diff --git a/examples/desktop_app/desktop_app.ecf b/examples/desktop_app/desktop_app.ecf new file mode 100644 index 00000000..78fd6e17 --- /dev/null +++ b/examples/desktop_app/desktop_app.ecf @@ -0,0 +1,28 @@ + + + Vision2+web browser widget+embedded web service + + This example demonstrates how to build a vision2 desktop application that embed a web browser accessing the service of an embedded web service. + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/examples/desktop_app/files/index.html b/examples/desktop_app/files/index.html new file mode 100644 index 00000000..345e6aef --- /dev/null +++ b/examples/desktop_app/files/index.html @@ -0,0 +1 @@ +Test diff --git a/examples/desktop_app/home.html b/examples/desktop_app/home.html new file mode 100644 index 00000000..26e7c6e2 --- /dev/null +++ b/examples/desktop_app/home.html @@ -0,0 +1,32 @@ + + + + + + +

This is a local file test with js

  • back to home
  • Let AJAX change this text

    + +
    + diff --git a/examples/desktop_app/src/app_embedded_web_service.e b/examples/desktop_app/src/app_embedded_web_service.e new file mode 100644 index 00000000..e9ebed88 --- /dev/null +++ b/examples/desktop_app/src/app_embedded_web_service.e @@ -0,0 +1,230 @@ +note + description: "Summary description for {APP_EMBEDDED_WEB_SERVICE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APP_EMBEDDED_WEB_SERVICE + +inherit + EMBEDDED_WEB_SERVICE + redefine + make + end + +create + make + +feature {NONE} -- Initialization + + make + do + Precursor + create request_exit_operation_actions + local_connection_restriction_enabled := True + end + +feature -- Execution + + request_exit_operation_actions: ACTION_SEQUENCE [TUPLE] + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + local + router: WSF_ROUTER + sess: detachable WSF_ROUTER_SESSION + m: WSF_HTML_PAGE_RESPONSE + b: STRING + fs: WSF_FILE_SYSTEM_HANDLER + do + create router.make (3) + router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test)) + router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env)) + router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit)) + create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files")) + router.handle ("/files", fs) + create sess + router.dispatch (req, res, sess) + if not sess.dispatched then + create m.make + create b.make_from_string ("

    Hello Eiffel desktop user

    ") + b.append ("
  • test
  • ") + b.append ("
  • env
  • ") + b.append ("
  • files
  • ") + b.append ("
  • exit
  • ") + m.set_body (b) + res.send (m) + end + end + + handle_test (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + b: STRING + l_name: READABLE_STRING_32 + do + if attached {WSF_STRING} req.item ("var") as p_name then + l_name := p_name.value + else + l_name := {STRING_32} "Embedded web service and web_browser in vision2 application" + end + create m.make + create b.make_from_string ("

    This is a test about "+ m.html_encoded_string (l_name) +"

    ") + b.append ("
  • back to home
  • ") + if l_name.is_case_insensitive_equal_general ("start") then + b.append ("
  • test javascript+ajax
  • ") + elseif l_name.is_case_insensitive_equal_general ("js") then + b.append ("[ +

    Let AJAX change this text

    + +
    + ]") + m.add_javascript_content ("[ + function loadXMLDoc() + { + var xmlhttp; + if (window.XMLHttpRequest) + {// code for IE7+, Firefox, Chrome, Opera, Safari + xmlhttp=new XMLHttpRequest(); + } + else + {// code for IE6, IE5 + xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); + } + xmlhttp.onreadystatechange=function() + { + if (xmlhttp.readyState==4 && xmlhttp.status==200) + { + document.getElementById("myDiv").innerHTML=xmlhttp.responseText; + } + } + xmlhttp.open("GET","/test/ajax.txt",true); + xmlhttp.send(); + } + ]") + elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then + b := "This is AJAX response ... from " + req.absolute_script_url ("") + end + m.set_body (b) + res.send (m) + end + + handle_env (req: WSF_REQUEST; res: WSF_RESPONSE) + local + s: STRING_8 + p: WSF_PAGE_RESPONSE + v: STRING_8 + do + create s.make (2048) + s.append ("**DEBUG**%N") + req.set_raw_input_data_recorded (True) + + append_iterable_to ("Meta variables:", req.meta_variables, s) + s.append_character ('%N') + + append_iterable_to ("Path parameters", req.path_parameters, s) + s.append_character ('%N') + + append_iterable_to ("Query parameters", req.query_parameters, s) + s.append_character ('%N') + + append_iterable_to ("Form parameters", req.form_parameters, s) + s.append_character ('%N') + + if attached req.content_type as l_type then + s.append ("Content: type=" + l_type.debug_output) + s.append (" length=") + s.append_natural_64 (req.content_length_value) + s.append_character ('%N') + create v.make (req.content_length_value.to_integer_32) + req.read_input_data_into (v) + across + v.split ('%N') as v_cursor + loop + s.append (" |") + s.append (v_cursor.item) + s.append_character ('%N') + end + end + + create p.make_with_body (s) + p.header.put_content_type_text_plain + res.send (p) + end + + handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + b: STRING + do + create m.make + create b.make_from_string ("

    Embedded server is about to shutdown

    ") + b.append ("
  • back to home
  • ") + m.set_body (b) + res.send (m) + if attached {WGI_NINO_CONNECTOR} req.wgi_connector as nino then + nino.server.shutdown_server + end + request_exit_operation_actions.call (Void) + end + +feature {NONE} -- Implementation + + append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8) + local + n: INTEGER + t: READABLE_STRING_8 + v: READABLE_STRING_8 + do + s.append (a_title) + s.append_character (':') + if it /= Void then + across it as c loop + n := n + 1 + end + if n = 0 then + s.append (" empty") + s.append_character ('%N') + else + s.append_character ('%N') + across + it as c + loop + s.append (" - ") + s.append (c.item.url_encoded_name) + t := c.item.generating_type + if t.same_string ("WSF_STRING") then + else + s.append_character (' ') + s.append_character ('{') + s.append (t) + s.append_character ('}') + end + s.append_character ('=') + v := c.item.string_representation.as_string_8 + if v.has ('%N') then + s.append_character ('%N') + across + v.split ('%N') as v_cursor + loop + s.append (" |") + s.append (v_cursor.item) + s.append_character ('%N') + end + else + s.append (v) + s.append_character ('%N') + end + end + end + else + s.append (" none") + s.append_character ('%N') + end + end + +end diff --git a/examples/desktop_app/src/desktop_app.e b/examples/desktop_app/src/desktop_app.e new file mode 100644 index 00000000..85aaf27c --- /dev/null +++ b/examples/desktop_app/src/desktop_app.e @@ -0,0 +1,71 @@ +note + description: "Objects that represent the Vision2 application.% + %The original version of this class has been generated by EiffelBuild." + generator: "EiffelBuild" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date: 2012-09-29 01:29:13 +0200 (sam., 29 sept. 2012) $" + revision: "$Revision: 89488 $" + + +class + DESKTOP_APP + +inherit + EV_APPLICATION + +create + make_and_launch + +feature {NONE} -- Initialization + + make_and_launch + -- Create `Current', build and display `main_window', + -- then launch the application. + local + l_win: like main_window + l_embeded_services: APP_EMBEDDED_WEB_SERVICE + do + default_create + create l_win.make + main_window := l_win + l_win.show + create l_embeded_services.make + l_embeded_services.set_port_number (0) -- Use first available port number + + l_embeded_services.on_launched_actions.force (agent on_web_service_launched (l_win)) + l_embeded_services.request_exit_operation_actions.force (agent on_quit) + l_embeded_services.launch + launch + end + + on_quit + do + if attached main_window as win then + win.destroy_and_exit_if_last + end + end + + on_web_service_launched (a_win: attached like main_window) + do + add_idle_action_kamikaze (agent a_win.open_link) + end + +feature {NONE} -- Implementation + + main_window: detachable MAIN_WINDOW + -- Main window of `Current' + +;note + copyright: "Copyright (c) 1984-2009, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 356 Storke Road, Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + + +end diff --git a/examples/desktop_app/src/main_window.e b/examples/desktop_app/src/main_window.e new file mode 100644 index 00000000..b259db99 --- /dev/null +++ b/examples/desktop_app/src/main_window.e @@ -0,0 +1,202 @@ +note + description: "Objects that represent an EV_TITLED_WINDOW.% + %The original version of this class was generated by EiffelBuild." + generator: "EiffelBuild" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date: 2010-08-17 10:49:12 +0200 (mar., 17 août 2010) $" + revision: "$Revision: 84189 $" + +class + MAIN_WINDOW + +inherit + EV_TITLED_WINDOW + redefine + create_interface_objects, initialize, is_in_default_state + end + + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + undefine + default_create, copy + end + +create + make + +feature {NONE} -- Initialization + + make + -- Creation method + do + default_create + end + + initialize + -- Initialize `Current'. + do + Precursor {EV_TITLED_WINDOW} + + set_title ("Desktop Application (demo embedded EWF+browser)") + + -- Connect events. + -- Close the application when an interface close + -- request is received on `Current'. i.e. the cross is clicked. + close_request_actions.extend (agent destroy_and_exit_if_last) + + -- Call `user_initialization'. + user_initialization + end + + create_interface_objects + -- Create objects + do + create home_button.make_with_text ("Home") + create back_button.make_with_text ("Back") + create forth_button.make_with_text ("Forth") + create refresh_button.make_with_text ("Refresh") + create stop_button.make_with_text ("Stop") + create url_text_field.make_with_text ("http://localhost:" + port_number.out) + create go_button.make_with_text ("Go") + + create web_browser + end + + user_initialization + -- Called by `initialize'. + -- Any custom user initialization that + -- could not be performed in `initialize', + -- (due to regeneration of implementation class) + -- can be added here. + local + l_browser_box: EV_VERTICAL_BOX + l_server_box: EV_VERTICAL_BOX + l_hor_box: EV_HORIZONTAL_BOX + vb: EV_VERTICAL_BOX + do + set_size (800, 600) + + create vb + extend (vb) + vb.set_border_width (3) + vb.set_padding_width (3) + + -- browser part + create l_browser_box + + create l_hor_box + l_browser_box.extend (l_hor_box) + l_browser_box.disable_item_expand (l_hor_box) + + home_button.select_actions.force_extend (agent on_home_button_action) + l_hor_box.extend (home_button) + l_hor_box.disable_item_expand (home_button) + + back_button.select_actions.force_extend (agent on_back_button_action) + l_hor_box.extend (back_button) + l_hor_box.disable_item_expand (back_button) + + forth_button.select_actions.force_extend (agent on_forth_button_action) + l_hor_box.extend (forth_button) + l_hor_box.disable_item_expand (forth_button) + + refresh_button.select_actions.force_extend (agent on_refresh_button_action) + l_hor_box.extend (refresh_button) + l_hor_box.disable_item_expand (refresh_button) + + stop_button.select_actions.force_extend (agent on_stop_button_action) + l_hor_box.extend (stop_button) + l_hor_box.disable_item_expand (stop_button) + + l_hor_box.extend (url_text_field) + + go_button.select_actions.force_extend (agent on_go_button_action) + l_hor_box.extend (go_button) + l_hor_box.disable_item_expand (go_button) + + l_browser_box.extend (web_browser) + + -------------------- + vb.extend (l_browser_box) + end + + is_in_default_state: BOOLEAN + do + Result := True + end + +feature -- Basic operation + + open_link + do + url_text_field.set_text ("http://localhost:" + port_number.out) + on_go_button_action + end + +feature {NONE} -- Implementation + + home_button, go_button, back_button, forth_button, stop_button, refresh_button: EV_BUTTON + -- Buttons + + url_text_field: EV_TEXT_FIELD + -- URL text field + + on_go_button_action + -- Action for `go_button' + local + l_uri: STRING_32 + do + l_uri := url_text_field.text + if l_uri /= Void and then not l_uri.is_empty then + web_browser.load_uri (l_uri) + else + on_home_button_action + end + end + + on_home_button_action + -- Action for `home_button' + do + web_browser.load_uri ("http://localhost:" + port_number.out) + end + + on_back_button_action + -- Action for `back_button' + do + web_browser.back + end + + on_forth_button_action + -- Action for `forth_button' + do + web_browser.forth + end + + on_refresh_button_action + -- Action for `refresh_button' + do + web_browser.refresh + end + + on_stop_button_action + -- Action for `stop_button' + do + web_browser.stop + end + + web_browser: EV_WEB_BROWSER + -- Web browser widget + +;note + copyright: "Copyright (c) 1984-2009, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 356 Storke Road, Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + + +end diff --git a/examples/desktop_app/src/service/embedded_web_service.e b/examples/desktop_app/src/service/embedded_web_service.e new file mode 100644 index 00000000..995f2393 --- /dev/null +++ b/examples/desktop_app/src/service/embedded_web_service.e @@ -0,0 +1,114 @@ +note + description: "Summary description for {EMBEDDED_WEB_SERVICE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + EMBEDDED_WEB_SERVICE + +inherit + THREAD + rename + make as make_thread, + execute as execute_thread + end + + WSF_SERVICE + rename + execute as execute_embedded + end + + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + +feature -- Initialization + + make + do + make_thread + create on_launched_actions + end + +feature {NONE} -- Execution + + execute_embedded (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + local + filter: WSF_AGENT_FILTER + m: WSF_PAGE_RESPONSE + do + if local_connection_restriction_enabled then + if + attached req.remote_addr as l_remote_addr and then + l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1") + then + execute (req, res) + else + create m.make_with_body ("Only local connection is allowed") + m.set_status_code (403) -- Forbidden + res.send (m) + end + else + execute (req, res) + end + end + + execute_thread + local + nino: WSF_NINO_SERVICE_LAUNCHER + opts: WSF_SERVICE_LAUNCHER_OPTIONS + do + create opts.default_create + opts.set_verbose (True) + opts.set_option ("port", port_number) + create nino.make (Current, opts) + nino.on_launched_actions.force (agent on_launched) + nino.launch + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + deferred + end + + on_launched (conn: WGI_CONNECTOR) + do + if attached {WGI_NINO_CONNECTOR} conn as nino then + set_port_number (nino.port) + end + on_launched_actions.call (Void) + end + +feature -- Control + + wait + -- Wait for server to be terminated. + do + join + end + +feature -- Access + + on_launched_actions: ACTION_SEQUENCE [TUPLE] + +feature -- Status report + + local_connection_restriction_enabled: BOOLEAN + -- Accept only local connection? + --| based on 127.0.0.1 IP + --| TO IMPROVE + +feature -- Change + + set_local_connection_restriction_enabled (b: BOOLEAN) + do + local_connection_restriction_enabled := b + end + +end diff --git a/examples/desktop_app/src/service/shared_embeded_web_service_information.e b/examples/desktop_app/src/service/shared_embeded_web_service_information.e new file mode 100644 index 00000000..ba04e855 --- /dev/null +++ b/examples/desktop_app/src/service/shared_embeded_web_service_information.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {SHARED_EMBEDED_WEB_SERVICE_INFORMATION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + +feature -- Access + + port_number: INTEGER + do + Result := port_number_cell.item + end + + set_port_number (a_port: like port_number) + do + port_number_cell.replace (a_port) + end + + port_number_cell: CELL [INTEGER] + once ("process") + create Result.put (0) + end + +end