Added: unomi/website/manual/1_9_x/index.html
URL: 
http://svn.apache.org/viewvc/unomi/website/manual/1_9_x/index.html?rev=1909975&view=auto
==============================================================================
--- unomi/website/manual/1_9_x/index.html (added)
+++ unomi/website/manual/1_9_x/index.html Mon May 22 09:30:01 2023
@@ -0,0 +1,9150 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="generator" content="Asciidoctor 1.5.8">
+<meta name="author" content="Apache Software Foundation">
+<title>Apache Unomi 1.x - Documentation</title>
+<link rel="stylesheet" href="./apache.css">
+</head>
+<body class="article toc2 toc-left">
+<div id="header">
+<h1>Apache Unomi 1.x - Documentation</h1>
+<div class="details">
+<span id="author" class="author">Apache Software Foundation</span><br>
+</div>
+<div id="toc" class="toc2">
+<div id="toctitle">Table of Contents</div>
+<ul class="sectlevel1">
+<li><a href="#_quick_start">1. Quick start</a>
+<ul class="sectlevel2">
+<li><a href="#_five_minutes_quickstart">1.1. Five Minutes QuickStart</a></li>
+</ul>
+</li>
+<li><a href="#_first_steps_with_apache_unomi">2. First steps with Apache 
Unomi</a>
+<ul class="sectlevel2">
+<li><a href="#_getting_started_with_unomi">2.1. Getting started with Unomi</a>
+<ul class="sectlevel3">
+<li><a href="#_prerequisites">2.1.1. Prerequisites</a></li>
+<li><a href="#_running_unomi">2.1.2. Running Unomi</a></li>
+</ul>
+</li>
+<li><a href="#_recipes">2.2. Recipes</a>
+<ul class="sectlevel3">
+<li><a href="#_introduction">2.2.1. Introduction</a></li>
+<li><a href="#_how_to_read_a_profile">2.2.2. How to read a profile</a></li>
+<li><a href="#_how_to_update_a_profile_from_the_public_internet">2.2.3. How to 
update a profile from the public internet</a></li>
+<li><a href="#_how_to_search_for_profile_events">2.2.4. How to search for 
profile events</a></li>
+<li><a href="#_how_to_create_a_new_rule">2.2.5. How to create a new 
rule</a></li>
+<li><a href="#_how_to_search_for_profiles">2.2.6. How to search for 
profiles</a></li>
+<li><a href="#_getting_updating_consents">2.2.7. Getting / updating 
consents</a></li>
+<li><a href="#_how_to_send_a_login_event_to_unomi">2.2.8. How to send a login 
event to Unomi</a></li>
+</ul>
+</li>
+<li><a href="#_request_examples">2.3. Request examples</a>
+<ul class="sectlevel3">
+<li><a href="#_retrieving_your_first_context">2.3.1. Retrieving your first 
context</a></li>
+<li><a href="#_retrieving_a_context_as_a_json_object">2.3.2. Retrieving a 
context as a JSON object.</a></li>
+<li><a href="#_accessing_profile_properties_in_a_context">2.3.3. Accessing 
profile properties in a context</a></li>
+<li><a href="#_sending_events_using_the_context_servlet">2.3.4. Sending events 
using the context servlet</a></li>
+<li><a href="#_sending_events_using_the_eventcollector_servlet">2.3.5. Sending 
events using the eventcollector servlet</a></li>
+<li><a href="#_where_to_go_from_here">2.3.6. Where to go from here</a></li>
+</ul>
+</li>
+<li><a href="#_web_tracker">2.4. Web Tracker</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started">2.4.1. Getting started</a></li>
+<li><a href="#_how_to_contribute">2.4.2. How to contribute</a></li>
+<li><a href="#_tracking_page_views">2.4.3. Tracking page views</a></li>
+<li><a href="#_tracking_form_submissions">2.4.4. Tracking form 
submissions</a></li>
+</ul>
+</li>
+<li><a href="#_configuration">2.5. Configuration</a>
+<ul class="sectlevel3">
+<li><a href="#_centralized_configuration">2.5.1. Centralized 
configuration</a></li>
+<li><a 
href="#_changing_the_default_configuration_using_environment_variables_i_e_docker_configuration">2.5.2.
 Changing the default configuration using environment variables (i.e. Docker 
configuration)</a></li>
+<li><a href="#_changing_the_default_configuration_using_property_files">2.5.3. 
Changing the default configuration using property files</a></li>
+<li><a href="#_secured_events_configuration">2.5.4. Secured events 
configuration</a></li>
+<li><a href="#_installing_the_maxmind_geoiplite2_ip_lookup_database">2.5.5. 
Installing the MaxMind GeoIPLite2 IP lookup database</a></li>
+<li><a href="#_installing_geonames_database">2.5.6. Installing Geonames 
database</a></li>
+<li><a href="#_rest_api_security">2.5.7. REST API Security</a></li>
+<li><a href="#_scripting_security">2.5.8. Scripting security</a></li>
+<li><a href="#_groovy_actions">2.5.9. Groovy Actions</a></li>
+<li><a href="#_scripting_roadmap">2.5.10. Scripting roadmap</a></li>
+<li><a href="#_automatic_profile_merging">2.5.11. Automatic profile 
merging</a></li>
+<li><a href="#_securing_a_production_environment">2.5.12. Securing a 
production environment</a></li>
+<li><a href="#_integrating_with_an_apache_http_web_server">2.5.13. Integrating 
with an Apache HTTP web server</a></li>
+<li><a href="#_changing_the_default_tracking_location">2.5.14. Changing the 
default tracking location</a></li>
+<li><a href="#_apache_karaf_ssh_console">2.5.15. Apache Karaf SSH 
Console</a></li>
+<li><a href="#_elasticsearch_authentication_and_security">2.5.16. 
ElasticSearch authentication and security</a></li>
+</ul>
+</li>
+<li><a href="#_useful_apache_unomi_urls">2.6. Useful Apache Unomi URLs</a></li>
+<li><a href="#_how_profile_tracking_works">2.7. How profile tracking works</a>
+<ul class="sectlevel3">
+<li><a href="#_steps">2.7.1. Steps</a></li>
+</ul>
+</li>
+<li><a href="#_context_request_flow">2.8. Context Request Flow</a></li>
+</ul>
+</li>
+<li><a href="#_queries_and_aggregations">3. Queries and aggregations</a>
+<ul class="sectlevel2">
+<li><a href="#_query_counts">3.1. Query counts</a></li>
+<li><a href="#_metrics">3.2. Metrics</a></li>
+<li><a href="#_aggregations">3.3. Aggregations</a>
+<ul class="sectlevel3">
+<li><a href="#_aggregation_types">3.3.1. Aggregation types</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_profile_import_export">4. Profile import &amp; export</a>
+<ul class="sectlevel2">
+<li><a href="#_importing_profiles">4.1. Importing profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_import_api">4.1.1. Import API</a></li>
+</ul>
+</li>
+<li><a href="#_exporting_profiles">4.2. Exporting profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_export_api">4.2.1. Export API</a></li>
+</ul>
+</li>
+<li><a href="#_configuration_in_details">4.3. Configuration in details</a></li>
+</ul>
+</li>
+<li><a href="#_consent_management">5. Consent management</a>
+<ul class="sectlevel2">
+<li><a href="#_consent_api">5.1. Consent API</a>
+<ul class="sectlevel3">
+<li><a href="#_profiles_with_consents">5.1.1. Profiles with consents</a></li>
+<li><a href="#_consent_type_definitions">5.1.2. Consent type 
definitions</a></li>
+<li><a href="#_creating_update_a_visitor_consent">5.1.3. Creating / update a 
visitor consent</a></li>
+<li><a href="#_how_it_works_internally">5.1.4. How it works 
(internally)</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_privacy_management">6. Privacy management</a>
+<ul class="sectlevel2">
+<li><a href="#_setting_up_access_to_the_privacy_endpoint">6.1. Setting up 
access to the privacy endpoint</a></li>
+<li><a href="#_anonymizing_a_profile">6.2. Anonymizing a profile</a></li>
+<li><a href="#_downloading_profile_data">6.3. Downloading profile data</a></li>
+<li><a href="#_deleting_a_profile">6.4. Deleting a profile</a></li>
+<li><a href="#_related">6.5. Related</a></li>
+</ul>
+</li>
+<li><a href="#_cluster_setup">7. Cluster setup</a>
+<ul class="sectlevel2">
+<li><a href="#_cluster_setup_2">7.1. Cluster setup</a></li>
+</ul>
+</li>
+<li><a href="#_reference">8. Reference</a>
+<ul class="sectlevel2">
+<li><a href="#_data_model_overview">8.1. Data Model Overview</a></li>
+<li><a href="#_scope">8.2. Scope</a>
+<ul class="sectlevel3">
+<li><a href="#_example">8.2.1. Example</a></li>
+</ul>
+</li>
+<li><a href="#_item">8.3. Item</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition">8.3.1. Structure definition</a></li>
+</ul>
+</li>
+<li><a href="#_metadata">8.4. Metadata</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_2">8.4.1. Structure definition</a></li>
+<li><a href="#_example_2">8.4.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_metadataitem">8.5. MetadataItem</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_3">8.5.1. Structure definition</a></li>
+<li><a href="#_example_3">8.5.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_event">8.6. Event</a>
+<ul class="sectlevel3">
+<li><a href="#_fields">8.6.1. Fields</a></li>
+<li><a href="#_event_types">8.6.2. Event types</a></li>
+</ul>
+</li>
+<li><a href="#_profile">8.7. Profile</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_4">8.7.1. Structure definition</a></li>
+<li><a href="#_example_4">8.7.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_persona">8.8. Persona</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_5">8.8.1. Structure definition</a></li>
+<li><a href="#_example_5">8.8.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_consent">8.9. Consent</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_6">8.9.1. Structure definition</a></li>
+<li><a href="#_example_6">8.9.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_session">8.10. Session</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_7">8.10.1. Structure definition</a></li>
+<li><a href="#_example_7">8.10.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_segment">8.11. Segment</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_8">8.11.1. Structure definition</a></li>
+<li><a href="#_example_8">8.11.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_condition">8.12. Condition</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_9">8.12.1. Structure definition</a></li>
+<li><a href="#_example_9">8.12.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_rule">8.13. Rule</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_10">8.13.1. Structure definition</a></li>
+<li><a href="#_example_10">8.13.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_action">8.14. Action</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_11">8.14.1. Structure definition</a></li>
+<li><a href="#_example_11">8.14.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_list">8.15. List</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_12">8.15.1. Structure definition</a></li>
+<li><a href="#_example_12">8.15.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_goal">8.16. Goal</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_13">8.16.1. Structure definition</a></li>
+<li><a href="#_example_13">8.16.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_campaign">8.17. Campaign</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_14">8.17.1. Structure definition</a></li>
+<li><a href="#_example_14">8.17.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_scoring_plan">8.18. Scoring plan</a>
+<ul class="sectlevel3">
+<li><a href="#_structure_definition_15">8.18.1. Structure definition</a></li>
+<li><a href="#_example_15">8.18.2. Example</a></li>
+</ul>
+</li>
+<li><a href="#_data_model_changes_for_apache_unomi_1_5_0">8.19. Data Model 
changes for Apache Unomi 1.5.0</a>
+<ul class="sectlevel3">
+<li><a href="#_data_model_and_elasticsearch_7">8.19.1. Data model and 
ElasticSearch 7</a></li>
+<li><a href="#_api_changes">8.19.2. API changes</a></li>
+</ul>
+</li>
+<li><a href="#_important_changes_in_public_servlets_since_version_1_5_5">8.20. 
Important changes in public servlets since version 1.5.5</a></li>
+<li><a href="#_built_in_event_types">8.21. Built-in Event types</a>
+<ul class="sectlevel3">
+<li><a href="#_login_event_type">8.21.1. Login event type</a></li>
+<li><a href="#_view_event_type">8.21.2. View event type</a></li>
+<li><a href="#_form_event_type">8.21.3. Form event type</a></li>
+<li><a href="#_update_properties_event_type">8.21.4. Update properties event 
type</a></li>
+<li><a href="#_identify_event_type">8.21.5. Identify event type</a></li>
+<li><a href="#_session_created_event_type">8.21.6. Session created event 
type</a></li>
+<li><a href="#_goal_event_type">8.21.7. Goal event type</a></li>
+<li><a href="#_modify_consent_event_type">8.21.8. Modify consent event 
type</a></li>
+</ul>
+</li>
+<li><a href="#_built_in_condition_types">8.22. Built-in condition types</a>
+<ul class="sectlevel3">
+<li><a href="#_existing_condition_type_descriptors">8.22.1. Existing condition 
type descriptors</a></li>
+</ul>
+</li>
+<li><a href="#_built_in_action_types">8.23. Built-in action types</a>
+<ul class="sectlevel3">
+<li><a href="#_existing_action_types_descriptors">8.23.1. Existing action 
types descriptors</a></li>
+</ul>
+</li>
+<li><a href="#_updating_events_using_the_context_servlet">8.24. Updating 
Events Using the Context Servlet</a>
+<ul class="sectlevel3">
+<li><a href="#_solution">8.24.1. Solution</a></li>
+<li><a href="#_defining_rules">8.24.2. Defining Rules</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_integration_samples">9. Integration samples</a>
+<ul class="sectlevel2">
+<li><a href="#_samples">9.1. Samples</a></li>
+<li><a href="#_login_sample">9.2. Login sample</a>
+<ul class="sectlevel3">
+<li><a href="#_warning">9.2.1. Warning !</a></li>
+<li><a href="#_installing_the_samples">9.2.2. Installing the samples</a></li>
+</ul>
+</li>
+<li><a href="#_twitter_sample">9.3. Twitter sample</a>
+<ul class="sectlevel3">
+<li><a href="#_overview">9.3.1. Overview</a></li>
+<li><a href="#_interacting_with_the_context_server">9.3.2. Interacting with 
the context server</a></li>
+<li><a 
href="#_retrieving_context_information_from_unomi_using_the_context_servlet">9.3.3.
 Retrieving context information from Unomi using the context servlet</a></li>
+</ul>
+</li>
+<li><a href="#_example_24">9.4. Example</a>
+<ul class="sectlevel3">
+<li><a href="#_html_page">9.4.1. HTML page</a></li>
+<li><a href="#_javascript">9.4.2. Javascript</a></li>
+</ul>
+</li>
+<li><a href="#_conclusion">9.5. Conclusion</a></li>
+<li><a href="#_annex">9.6. Annex</a></li>
+<li><a href="#_weather_update_sample">9.7. Weather update sample</a></li>
+</ul>
+</li>
+<li><a href="#_connectors">10. Connectors</a>
+<ul class="sectlevel2">
+<li><a href="#_connectors_2">10.1. Connectors</a>
+<ul class="sectlevel3">
+<li><a href="#_call_for_contributors">10.1.1. Call for contributors</a></li>
+</ul>
+</li>
+<li><a href="#_salesforce_connector">10.2. Salesforce Connector</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started_2">10.2.1. Getting started</a></li>
+<li><a href="#_properties">10.2.2. Properties</a></li>
+<li><a 
href="#_hot_deploying_updates_to_the_salesforce_connector_for_developers">10.2.3.
 Hot-deploying updates to the Salesforce connector (for developers)</a></li>
+<li><a href="#_using_the_salesforce_workbench_for_testing_rest_api">10.2.4. 
Using the Salesforce Workbench for testing REST API</a></li>
+<li><a href="#_setting_up_streaming_push_queries">10.2.5. Setting up Streaming 
Push queries</a></li>
+<li><a href="#_executing_the_unit_tests">10.2.6. Executing the unit 
tests</a></li>
+</ul>
+</li>
+<li><a href="#_mailchimp_connector">10.3. MailChimp Connector</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started_3">10.3.1. Getting started</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_developers">11. Developers</a>
+<ul class="sectlevel2">
+<li><a href="#_building">11.1. Building</a>
+<ul class="sectlevel3">
+<li><a href="#_initial_setup">11.1.1. Initial Setup</a></li>
+<li><a href="#_building_2">11.1.2. Building</a></li>
+<li><a href="#_installing_an_elasticsearch_server">11.1.3. Installing an 
ElasticSearch server</a></li>
+<li><a href="#_deploying_the_generated_binary_package">11.1.4. Deploying the 
generated binary package</a></li>
+<li><a href="#_deploying_into_an_existing_karaf_server">11.1.5. Deploying into 
an existing Karaf server</a></li>
+<li><a href="#_jdk_selection_on_mac_os_x">11.1.6. JDK Selection on Mac OS 
X</a></li>
+<li><a href="#_running_the_integration_tests">11.1.7. Running the integration 
tests</a></li>
+<li><a href="#_testing_with_an_example_page">11.1.8. Testing with an example 
page</a></li>
+</ul>
+</li>
+<li><a href="#_ssh_shell_commands">11.2. SSH Shell Commands</a>
+<ul class="sectlevel3">
+<li><a href="#_using_the_shell">11.2.1. Using the shell</a></li>
+<li><a href="#_lifecycle_commands">11.2.2. Lifecycle commands</a></li>
+<li><a href="#_runtime_commands">11.2.3. Runtime commands</a></li>
+</ul>
+</li>
+<li><a href="#_writing_plugins">11.3. Writing Plugins</a></li>
+<li><a href="#_types_vs_instances">11.4. Types vs. instances</a></li>
+<li><a href="#_plugin_structure">11.5. Plugin structure</a></li>
+<li><a href="#_extension_points">11.6. Extension points</a>
+<ul class="sectlevel3">
+<li><a href="#_actiontype">11.6.1. ActionType</a></li>
+<li><a href="#_conditiontype">11.6.2. ConditionType</a></li>
+<li><a href="#_persona_2">11.6.3. Persona</a></li>
+<li><a href="#_propertymergestrategytype">11.6.4. 
PropertyMergeStrategyType</a></li>
+<li><a href="#_propertytype">11.6.5. PropertyType</a></li>
+<li><a href="#_rule_2">11.6.6. Rule</a></li>
+<li><a href="#_scoring">11.6.7. Scoring</a></li>
+<li><a href="#_segments">11.6.8. Segments</a></li>
+<li><a href="#_tag">11.6.9. Tag</a></li>
+<li><a href="#_valuetype">11.6.10. ValueType</a></li>
+</ul>
+</li>
+<li><a href="#_custom_plugins">11.7. Custom plugins</a>
+<ul class="sectlevel3">
+<li><a href="#_creating_a_plugin">11.7.1. Creating a plugin</a></li>
+<li><a href="#_deployment_and_custom_definition">11.7.2. Deployment and custom 
definition</a></li>
+<li><a href="#_predefined_segments">11.7.3. Predefined segments</a></li>
+<li><a href="#_predefined_rules">11.7.4. Predefined rules</a></li>
+<li><a href="#_predefined_properties">11.7.5. Predefined properties</a></li>
+<li><a href="#_predefined_child_conditions">11.7.6. Predefined child 
conditions</a></li>
+<li><a href="#_predefined_personas">11.7.7. Predefined personas</a></li>
+<li><a href="#_custom_action_types">11.7.8. Custom action types</a></li>
+<li><a href="#_custom_condition_types">11.7.9. Custom condition types</a></li>
+</ul>
+</li>
+<li><a href="#_migration_patches">11.8. Migration patches</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div id="content">
+<div id="preamble">
+<div class="sectionbody">
+<div class="imageblock text-center">
+<div class="content">
+<img src="images/asf_logo_url.png" alt="asf logo url">
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_quick_start">1. Quick start</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_five_minutes_quickstart">1.1. Five Minutes QuickStart</h3>
+<div class="paragraph">
+<p>1) Install JDK 8 (<a 
href="https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html";
 
class="bare">https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html</a>)
 and make sure you set the
+JAVA_HOME variable <a 
href="https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/"; 
class="bare">https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/</a>
 (see our <a href="#_jdk_compatibility">Getting Started</a> guide for more 
information on JDK compatibility)</p>
+</div>
+<div class="paragraph">
+<p>2) Download ElasticSearch here : <a 
href="https://www.elastic.co/downloads/past-releases/elasticsearch-7-4-2"; 
class="bare">https://www.elastic.co/downloads/past-releases/elasticsearch-7-4-2</a>
 (please &lt;strong&gt;make sure&lt;/strong&gt; you use the proper version : 
7.4.2)</p>
+</div>
+<div class="paragraph">
+<p>3) Uncompress it and change the <code>config/elasticsearch.yml</code> to 
include the following config : &lt;code&gt;cluster.name: 
contextElasticSearch&lt;/code&gt;</p>
+</div>
+<div class="paragraph">
+<p>4) Launch ElasticSearch using : <code>bin/elasticsearch</code></p>
+</div>
+<div class="paragraph">
+<p>5) Download Apache Unomi here : <a 
href="https://unomi.apache.org/download.html"; 
class="bare">https://unomi.apache.org/download.html</a></p>
+</div>
+<div class="paragraph">
+<p>6) Start it using : <code>./bin/karaf</code></p>
+</div>
+<div class="paragraph">
+<p>7) Start the Apache Unomi packages using <code>unomi:start</code> in the 
Apache Karaf Shell</p>
+</div>
+<div class="paragraph">
+<p>8) Wait for startup to complete</p>
+</div>
+<div class="paragraph">
+<p>9) Try accessing <a href="https://localhost:9443/cxs/cluster"; 
class="bare">https://localhost:9443/cxs/cluster</a> with username/password: 
<code>karaf/karaf</code> . You might get a certificate warning in your browser, 
just accept it despite the warning it is safe.</p>
+</div>
+<div class="paragraph">
+<p>10) Request your first context by simply accessing : <a 
href="http://localhost:8181/context.js?sessionId=1234"; 
class="bare">http://localhost:8181/context.js?sessionId=1234</a></p>
+</div>
+<div class="paragraph">
+<p>11) If something goes wrong, you should check the logs in 
<code>./data/log/karaf.log</code>. If you get errors on ElasticSearch,
+make sure you are using the proper version.</p>
+</div>
+<div class="paragraph">
+<p>Next steps:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Connect to <a href="http://localhost:8181"; 
class="bare">http://localhost:8181</a> to try our some live examples (such as 
the web tracker)</p>
+</li>
+<li>
+<p>Trying our integration <a href="#_samples">samples page</a></p>
+</li>
+<li>
+<p>Learning more about the <a href="#_web_tracker">web tracker</a></p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_first_steps_with_apache_unomi">2. First steps with Apache Unomi</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_getting_started_with_unomi">2.1. Getting started with Unomi</h3>
+<div class="paragraph">
+<p>We will first get you up and running with an example. We will then lift the 
corner of the cover somewhat and explain
+in greater details what just happened.</p>
+</div>
+<div class="sect3">
+<h4 id="_prerequisites">2.1.1. Prerequisites</h4>
+<div class="paragraph">
+<p>This document assumes working knowledge of <a 
href="https://git-scm.com/";>git</a> to be able to retrieve the code for Unomi 
and the example.
+Additionally, you will require a working Java 8 or above install. Refer to <a 
href="http://www.oracle.com/technetwork/java/javase/";>http://www.oracle.com/technetwork/java/javase/</a>
 for details on how to download and install Java SE 8 or greater.</p>
+</div>
+<div class="sect4">
+<h5 id="_jdk_compatibility">JDK compatibility</h5>
+<div class="paragraph">
+<p>Starting with Java 9, Oracle made some big changes to the Java platform 
releases. This is why Apache Unomi is focused on
+supporting the Long Term Supported versions of the JDK, currently versions 8 
and 11. We do not test with intermediate
+versions so they may or may not work properly. Currently the most tested 
version is version 8 and version 11 is also
+supported.</p>
+</div>
+<div class="paragraph">
+<p>Also, as there are new licensing restrictions on JDKs provided by Oracle 
for production usages, Apache Unomi has also
+added support for OpenJDK builds. Other JDK distributions might also work but 
are not regularly tested so you should use
+them at your own risks.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_elasticsearch_compatibility">ElasticSearch compatibility</h5>
+<div class="paragraph">
+<p>Starting with version 1.5.0 Apache Unomi adds compatibility with 
ElasticSearch 7.4 . It is highly recommended to use the
+ElasticSearch version provided by the documentation when possible. However 
minor versions (7.4.x) should also work, and
+one version higher (7.5) will usually work. Going higher than that is risky 
given the way that ElasticSearch is developed
+and breaking changes are introduced quite often. If in doubt, don&#8217;t 
hesitate to check with the Apache Unomi community
+to get the latest information about ElasticSearch version compatibility.</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_running_unomi">2.1.2. Running Unomi</h4>
+<div class="sect4">
+<h5 id="_start_unomi">Start Unomi</h5>
+<div class="paragraph">
+<p>Start Unomi according to the <a href="#_five_minutes_quickstart">five 
minutes quick start</a> or by compiling using the
+<a href="#_building">building instructions</a>. Once you have Karaf running,
+ you should wait until you see the following messages on the Karaf console:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>Initializing user list service endpoint...
+Initializing geonames service endpoint...
+Initializing segment service endpoint...
+Initializing scoring service endpoint...
+Initializing campaigns service endpoint...
+Initializing rule service endpoint...
+Initializing profile service endpoint...
+Initializing cluster service endpoint...</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This indicates that all the Unomi services are started and ready to react 
to requests. You can then open a browser and go to <code><a 
href="http://localhost:8181/cxs"; 
class="bare">http://localhost:8181/cxs</a></code> to see the list of
+available RESTful services or retrieve an initial context at <code><a 
href="http://localhost:8181/context.json"; 
class="bare">http://localhost:8181/context.json</a></code> (which isn&#8217;t 
very useful at this point).</p>
+</div>
+<div class="paragraph">
+<p>You can now find an introduction page at the following location: <a 
href="http://localhost:8181"; class="bare">http://localhost:8181</a></p>
+</div>
+<div class="paragraph">
+<p>Also now that your service is up and running you can go look at the
+<a href="#_request_examples">request examples</a> to learn basic
+requests you can do once your server is up and running.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_recipes">2.2. Recipes</h3>
+<div class="sect3">
+<h4 id="_introduction">2.2.1. Introduction</h4>
+<div class="paragraph">
+<p>In this section of the documentation we provide quick recipes focused on 
helping you achieve a specific result with
+Apache Unomi.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_read_a_profile">2.2.2. How to read a profile</h4>
+<div class="paragraph">
+<p>The simplest way to retrieve profile data for the current profile is to 
simply send a request to the /context.json
+endpoint. However you will need to send a body along with that request. 
Here&#8217;s an example:</p>
+</div>
+<div class="paragraph">
+<p>Here is an example that will retrieve all the session and profile 
properties, as well as the profile&#8217;s segments and scores</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST 
http://localhost:8181/context.json?sessionId=1234 \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+    "source": {
+        "itemId":"homepage",
+        "itemType":"page",
+        "scope":"example"
+    },
+    "requiredProfileProperties":["*"],
+    "requiredSessionProperties":["*"],
+    "requireSegments":true,
+    "requireScores":true
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The <code>requiredProfileProperties</code> and 
<code>requiredSessionProperties</code> are properties that take an array of 
property names
+that should be retrieved. In this case we use the wildcard character '*' to 
say we want to retrieve all the available
+properties. The structure of the JSON object that you should send is a 
JSON-serialized version of the <a 
href="http://unomi.apache.org/unomi-api/apidocs/org/apache/unomi/api/ContextRequest.html";>ContextRequest</a>
+Java class.</p>
+</div>
+<div class="paragraph">
+<p>Note that it is also possible to access a profile&#8217;s data through the 
/cxs/profiles/ endpoint but that really should be
+reserved to administrative purposes. All public accesses should always use the 
/context.json endpoint for consistency
+and security.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_update_a_profile_from_the_public_internet">2.2.3. How to 
update a profile from the public internet</h4>
+<div class="paragraph">
+<p>Before we get into how to update a profile directly from a request coming 
from the public internet, we&#8217;ll quickly talk
+first about how NOT to do it, because we often see users using the following 
anti-patterns.</p>
+</div>
+<div class="sect4">
+<h5 id="_how_not_to_update_a_profile_from_the_public_internet">How NOT to 
update a profile from the public internet</h5>
+<div class="paragraph">
+<p>Please avoid using the /cxs/profile endpoint. This endpoint was initially 
the only way to update a profile but it has
+multiple issues:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>it requires authenticated access. The temptation can be great to use this 
endpoint because it is simple to access
+but the risk is that developers might include the credentials to access it in 
non-secure parts of code such as
+client-side code. Since there is no difference between this endpoint and any 
other administration-focused endpoints,
+attackers could easily re-use stolen credentials to wreak havock on the whole 
platform.</p>
+</li>
+<li>
+<p>No history of profile modifications is kept: this can be a problem for 
multiple reasons: you might want to keep an
+trail of profile modifications, or even a history of profile values in case 
you want to understand how a profile
+property was modified.</p>
+</li>
+<li>
+<p>Even when protected using some kind of proxy, potentially the whole profile 
properties might be modified, including
+ones that you might not want to be overriden.</p>
+</li>
+</ul>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_recommended_ways_to_update_a_profile">Recommended ways to update a 
profile</h5>
+<div class="paragraph">
+<p>Instead you can use the following solutions to update profiles:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>(Preferred) Use you own custom event(s) to send data you want to be 
inserted in a profile, and use rules to map the
+event data to the profile. This is simpler than it sounds, as usually all it 
requires is setting up a simple rule and
+you&#8217;re ready to update profiles using events. This is also the safest 
way to update a profile because if you design your
+events to be as specific as possible to your needs, only the data that you 
specified will be copied to the profile,
+making sure that even in the case an attacker tries to send more data using 
your custom event it will simply be ignored.</p>
+</li>
+<li>
+<p>Use the protected built-in "updateProperties" event. This event is designed 
to be used for administrative purposes
+only. Again, prefer the custom events solution because as this is a protected 
event it will require sending the Unomi
+key as a request header, and as Unomi only supports a single key for the 
moment it could be problematic if the key is
+intercepted. But at least by using an event you will get the benefits of 
auditing and historical property modification
+tracing.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Let&#8217;s go into more detail about the preferred way to update a 
profile. Let&#8217;s consider the following example of a rule:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/rules \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+  "metadata": {
+    "id": "setContactInfo",
+    "name": "Copy the received contact info to the current profile",
+    "description": "Copies the contact info received in a custom event called 
'contactInfoSubmitted' to the current profile"
+  },
+  "raiseEventOnlyOnceForSession": false,
+  "condition": {
+    "type": "eventTypeCondition",
+    "parameterValues": {
+      "eventTypeId": "contactInfoSubmitted"
+    }
+  },
+  "actions": [
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(firstName)",
+        "setPropertyValue": "eventProperty::properties(firstName)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    },
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(lastName)",
+        "setPropertyValue": "eventProperty::properties(lastName)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    },
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(email)",
+        "setPropertyValue": "eventProperty::properties(email)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    }
+  ]
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>What this rule does is that it listen for a custom event (events 
don&#8217;t need any registration, you can simply start
+sending them to Apache Unomi whenever you like) of type 'contactInfoSubmitted' 
and it will search for properties called
+'firstName', 'lastName' and 'email' and copy them over to the profile with 
corresponding property names. You could of
+course change any of the property names to find your needs. For example you 
might want to prefix the profile properties
+with the source of the event, such as 'mobileApp:firstName'.</p>
+</div>
+<div class="paragraph">
+<p>You could then simply send the <code>contactInfoSubmitted</code> event 
using a request similar to this one:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/eventcollector 
\
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+    "sessionId" : "1234",
+    "events":[
+        {
+            "eventType":"contactInfoSubmitted",
+            "scope": "example",
+            "source":{
+                "itemType": "site",
+                "scope":"example",
+                "itemId": "mysite"
+            },
+            "target":{
+                "itemType":"form",
+                "scope":"example",
+                "itemId":"contactForm"
+            },
+            "properties" : {
+              "firstName" : "John",
+              "lastName" : "Doe",
+              "email" : "[email protected]"
+            }
+        }
+    ]
+}
+EOF</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_search_for_profile_events">2.2.4. How to search for profile 
events</h4>
+<div class="paragraph">
+<p>Sometimes you want to retrieve events for a known profile. You will need to 
provide a query in the body of the request
+that looks something like this (and <a 
href="https://unomi.apache.org/rest-api-doc/#1768188821";>documentation is 
available in the REST API</a>) :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST 
http://localhost:8181/cxs/events/search \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{ "offset" : 0,
+  "limit" : 20,
+  "condition" : {
+    "type": "eventPropertyCondition",
+    "parameterValues" : {
+      "propertyName" : "profileId",
+      "comparisonOperator" : "equals",
+      "propertyValue" : "PROFILE_ID"
+    }
+  }
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>where PROFILE_ID is a profile identifier. This will indeed retrieve all the 
events for a given profile.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_create_a_new_rule">2.2.5. How to create a new rule</h4>
+<div class="paragraph">
+<p>There are basically two ways to create a new rule :</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Using the REST API</p>
+</li>
+<li>
+<p>Packaging it as a predefined rule in a plugin</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>In both cases the JSON structure for the rule will be exactly the same, and 
in most scenarios it will be more
+interesting to use the REST API to create and manipulate rules, as they 
don&#8217;t require any development or deployments
+on the Apache Unomi server.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/rules \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+  "metadata": {
+    "id": "exampleEventCopy",
+    "name": "Example Copy Event to Profile",
+    "description": "Copy event properties to profile properties"
+  },
+  "condition": {
+      "type": "eventTypeCondition",
+      "parameterValues": {
+        "eventTypeId" : "myEvent"
+      }
+  },
+  "actions": [
+    {
+      "parameterValues": {
+      },
+      "type": "allEventToProfilePropertiesAction"
+    }
+  ]
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The above rule will be executed if the incoming event is of type 
<code>myEvent</code> and will simply copy all the properties
+contained in the event to the current profile.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_search_for_profiles">2.2.6. How to search for profiles</h4>
+<div class="paragraph">
+<p>In order to search for profiles you will have to use the 
/cxs/profiles/search endpoint that requires a Query JSON
+structure. Here&#8217;s an example of a profile search with a Query object:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST 
http://localhost:8181/cxs/profiles/search \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+  "text" : "unomi",
+  "offset" : 0,
+  "limit" : 10,
+  "sortby" : "properties.lastName:asc,properties.firstName:desc",
+  "condition" : {
+    "type" : "booleanCondition",
+    "parameterValues" : {
+      "operator" : "and",
+      "subConditions" : [
+        {
+          "type": "profilePropertyCondition",
+          "parameterValues": {
+            "propertyName": "properties.leadAssignedTo",
+            "comparisonOperator": "exists"
+          }
+        },
+        {
+          "type": "profilePropertyCondition",
+          "parameterValues": {
+            "propertyName": "properties.lastName",
+            "comparisonOperator": "exists"
+          }
+        }
+      ]
+    }
+  }
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>In the above example, you search for all the profiles that have the 
<code>leadAssignedTo</code> and <code>lastName</code> properties and that
+have the <code>unomi</code> value anywhere in their profile property values. 
You are also specifying that you only want 10 results
+beginning at offset 0. The results will be also sorted in alphabetical order 
for the <code>lastName</code> property value, and then
+by reverse alphabetical order for the <code>firstName</code> property 
value.</p>
+</div>
+<div class="paragraph">
+<p>As you can see, queries can be quite complex. Please remember that the more 
complex the more resources it will consume
+on the server and potentially this could affect performance.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_getting_updating_consents">2.2.7. Getting / updating consents</h4>
+<div class="paragraph">
+<p>You can find information on how to retrieve or create/update consents in 
the <a href="#_consent_api">Consent API</a> section.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_send_a_login_event_to_unomi">2.2.8. How to send a login event 
to Unomi</h4>
+<div class="paragraph">
+<p>Tracking logins must be done carefully with Unomi. A login event is 
considered a "privileged" event and therefore for
+not be initiated from the public internet. Ideally user authentication should 
always be validated by a trusted third-
+party even if it is a well-known social platform such as Facebook or Twitter. 
Basically what should NEVER be done:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Login to a social platform</p>
+</li>
+<li>
+<p>Call back to the originating page</p>
+</li>
+<li>
+<p>Send a login event to Unomi from the page originating the login in step 
1</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>The problem with this, is that any attacker could simply directly call step 
3 without any kind of security. Instead the
+flow should look something like this:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Login to a social platform</p>
+</li>
+<li>
+<p>Call back to a special secured system that performs an server-to-server 
call to send the login event to Apache
+Unomi using the Unomi key.</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>For simplicity reasons, in our login example, the first method is used, but 
it really should never be done like this
+in production because of the aforementioned security issues. The second 
method, although a little more involved, is
+much preferred.</p>
+</div>
+<div class="paragraph">
+<p>When sending a login event, you can setup a rule that can check a profile 
property to see if profiles can be merged on an
+universal identifier such as an email address.</p>
+</div>
+<div class="paragraph">
+<p>In our login sample we provide an example of such a rule. You can find it 
here:</p>
+</div>
+<div class="paragraph">
+<p><a 
href="https://github.com/apache/unomi/blob/master/samples/login-integration/src/main/resources/META-INF/cxs/rules/exampleLogin.json";
 
class="bare">https://github.com/apache/unomi/blob/master/samples/login-integration/src/main/resources/META-INF/cxs/rules/exampleLogin.json</a></p>
+</div>
+<div class="paragraph">
+<p>As you can see in this rule, we call an action called :</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>mergeProfilesOnPropertyAction</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>with as a parameter value the name of the property on which to perform the 
merge (the email). What this means is that
+upon successful login using an email, Unomi will look for other profiles that 
have the same email and merge them into
+a single profile. Because of the merge, this should only be done for 
authenticated profiles, otherwise this could be a
+security issue since it could be a way to load data from other profiles by 
merging their data !</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_request_examples">2.3. Request examples</h3>
+<div class="sect3">
+<h4 id="_retrieving_your_first_context">2.3.1. Retrieving your first 
context</h4>
+<div class="paragraph">
+<p>You can retrieve a context using curl like this :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl 
http://localhost:8181/context.js?sessionId=1234</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This will retrieve a JavaScript script that contains a <code>cxs</code> 
object that contains the context with the current user
+profile, segments, scores as well as functions that makes it easier to perform 
further requests (such as collecting
+events using the cxs.collectEvents() function).</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_retrieving_a_context_as_a_json_object">2.3.2. Retrieving a context as 
a JSON object.</h4>
+<div class="paragraph">
+<p>If you prefer to retrieve a pure JSON object, you can simply use a request 
formed like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl 
http://localhost:8181/context.json?sessionId=1234</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_accessing_profile_properties_in_a_context">2.3.3. Accessing profile 
properties in a context</h4>
+<div class="paragraph">
+<p>By default, in order to optimize the amount of data sent over the network, 
Apache Unomi will not send the content of
+the profile or session properties. If you need this data, you must send a JSON 
object to configure the resulting output
+of the context.js(on) servlet.</p>
+</div>
+<div class="paragraph">
+<p>Here is an example that will retrieve all the session and profile 
properties, as well as the profile&#8217;s segments and
+scores</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST 
http://localhost:8181/context.json?sessionId=1234 \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+    "source": {
+        "itemId":"homepage",
+        "itemType":"page",
+        "scope":"example"
+    },
+    "requiredProfileProperties":["*"],
+    "requiredSessionProperties":["*"],
+    "requireSegments":true,
+    "requireScores":true
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The <code>requiredProfileProperties</code> and 
<code>requiredSessionProperties</code> are properties that take an array of 
property names
+that should be retrieved. In this case we use the wildcard character '*' to 
say we want to retrieve all the available
+properties. The structure of the JSON object that you should send is a 
JSON-serialized version of the <a 
href="http://unomi.apache.org/unomi-api/apidocs/org/apache/unomi/api/ContextRequest.html";>ContextRequest</a>
+Java class.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_sending_events_using_the_context_servlet">2.3.4. Sending events using 
the context servlet</h4>
+<div class="paragraph">
+<p>At the same time as you are retrieving the context, you can also directly 
send events in the ContextRequest object as
+illustrated in the following example:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST 
http://localhost:8181/context.json?sessionId=1234 \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+    "source":{
+        "itemId":"homepage",
+        "itemType":"page",
+        "scope":"example"
+    },
+    "events":[
+        {
+            "eventType":"view",
+            "scope": "example",
+            "source":{
+                "itemType": "site",
+                "scope":"example",
+                "itemId": "mysite"
+            },
+            "target":{
+                "itemType":"page",
+                "scope":"example",
+                "itemId":"homepage",
+                "properties":{
+                    "pageInfo":{
+                        "referringURL":""
+                    }
+                }
+            }
+        }
+    ]
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Upon received events, Apache Unomi will execute all the rules that match 
the current context, and return an updated context.
+This way of sending events is usually used upon first loading of a page. If 
you want to send events after the page has
+finished loading you could either do a second call and get an updating 
context, or if you don&#8217;t need the context and want
+to send events in a network optimal way you can use the eventcollector servlet 
(see below).</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_sending_events_using_the_eventcollector_servlet">2.3.5. Sending 
events using the eventcollector servlet</h4>
+<div class="paragraph">
+<p>If you only need to send events without retrieving a context, you should 
use the eventcollector servlet that is optimized
+respond quickly and minimize network traffic. Here is an example of using this 
servlet:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/eventcollector 
\
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+    "sessionId" : "1234",
+    "events":[
+        {
+            "eventType":"view",
+            "scope": "example",
+            "source":{
+                "itemType": "site",
+                "scope":"example",
+                "itemId": "mysite"
+            },
+            "target":{
+                "itemType":"page",
+                "scope":"example",
+                "itemId":"homepage",
+                "properties":{
+                    "pageInfo":{
+                        "referringURL":""
+                    }
+                }
+            }
+        }
+    ]
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Note that the eventcollector executes the rules but does not return a 
context. If is generally used after a page is loaded
+to send additional events.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_where_to_go_from_here">2.3.6. Where to go from here</h4>
+<div class="ulist">
+<ul>
+<li>
+<p>You can find more <a href="#_useful_apache_unomi_urls">useful Apache Unomi 
URLs</a> that can be used in the same way as the above examples.</p>
+</li>
+<li>
+<p>You may want to know integrate the provided <a href="#_web_tracker">web 
tracker</a> into your web site.</p>
+</li>
+<li>
+<p>Read the <a href="#_twitter_sample">Twitter sample</a> documentation that 
contains a detailed example of how to integrate with Apache Unomi.</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_web_tracker">2.4. Web Tracker</h3>
+<div class="paragraph">
+<p>This extension is providing the web tracker to start collecting visitors 
data on your website.
+The tracker is implemented as an integration of <a 
href="https://github.com/segmentio/analytics.js";>analytics.js</a> for Unomi.</p>
+</div>
+<div class="sect3">
+<h4 id="_getting_started">2.4.1. Getting started</h4>
+<div class="paragraph">
+<p>Extension can be tested at : <code><a 
href="http://localhost:8181/tracker/index.html"; 
class="bare">http://localhost:8181/tracker/index.html</a></code></p>
+</div>
+<div class="paragraph">
+<p>In your page include unomiOptions and include code snippet from 
<code>snippet.min.js</code> :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>&lt;script type="text/javascript"&gt;
+        var unomiOption = {
+            scope: 'realEstateManager',
+            url: 'http://localhost:8181'
+        };
+        window.unomiTracker||(window.unomiTracker={}),function(){function 
e(e){for(unomiTracker.initialize({"Apache 
Unomi":unomiOption});n.length&gt;0;){var 
r=n.shift(),t=r.shift();unomiTracker[t]&amp;&amp;unomiTracker[t].apply(unomiTracker,r)}}for(var
 
n=[],r=["trackSubmit","trackClick","trackLink","trackForm","initialize","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","personalize"],t=0;t&lt;r.length;t++){var
 i=r[t];window.unomiTracker[i]=function(e){return function(){var 
r=Array.prototype.slice.call(arguments);return 
r.unshift(e),n.push(r),window.unomiTracker}}(i)}unomiTracker.load=function(){var
 
n=document.createElement("script");n.type="text/javascript",n.async=!0,n.src=unomiOption.url+"/tracker/unomi-tracker.min.js",n.addEventListener?n.addEventListener("load",function(n){"function"==typeof
 
e&amp;&amp;e(n)},!1):n.onreadystatechange=function(){"complete"!==this.readyState&amp;&amp;"loaded"!==this.readyState||e(window.event)};var
 r=
 
document.getElementsByTagName("script")[0];r.parentNode.insertBefore(n,r)},document.addEventListener("DOMContentLoaded",unomiTracker.load),unomiTracker.page()}();
+&lt;/script&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><code>window.unomiTracker</code> can be used to send additional events when 
needed.</p>
+</div>
+<div class="paragraph">
+<p>Check analytics.js API <a 
href="https://segment.com/docs/sources/website/analytics.js/";>here</a>.
+All methods can be used on <code>unomiTracker</code> object, although not all 
event types are supported by Unomi intergation.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_contribute">2.4.2. How to contribute</h4>
+<div class="paragraph">
+<p>The source code is in the folder javascript with a package.json, the file 
to update is <code>analytics.js-integration-apache-unomi.js</code> apply your 
modification in this file then use the command <code>yarn build</code> to 
compile a new JS file.
+Then you can use the test page to try your changes <code><a 
href="http://localhost:8181/tracker/index.html"; 
class="bare">http://localhost:8181/tracker/index.html</a></code>.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_tracking_page_views">2.4.3. Tracking page views</h4>
+<div class="paragraph">
+<p>In the initialize call, the tracker will generate an implicit page view 
event, which by default will be populated with
+the following information:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" 
data-lang="javascript">    window.digitalData.page = window.digitalData.page || 
{
+        path: location.pathname + location.hash,
+        pageInfo: {
+            pageName: document.title,
+            pageID : location.pathname + location.hash,
+            pagePath : location.pathname + location.hash,
+            destinationURL: location.href
+        }
+    }</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Now if you want to provide your own custom page information for the initial 
page view, you can simply do it like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-`javascript" 
data-lang="`javascript">    unomiTracker.initialize({
+            scope: 'myScope',
+            url: 'http://unomi:8181', // we use an empty URL to make it 
relative to this page.
+            initialPageProperties: {
+                path: path,
+                pageInfo: {
+                    destinationURL: location.href,
+                    tags: ["tag1", "tag2", "tag3"],
+                    categories: ["category1", "category2", "category3"]
+                },
+                interests: {
+                    "interest1": 1,
+                    "interest2": 2,
+                    "interest3": 3
+                }
+            }
+        });</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>`</p>
+</div>
+<div class="paragraph">
+<p>Also note that the FIRST call to unomiTracker.page() will be IGNORED 
because of this initial page view.This is the
+way that the Analytics.js library handles it.So make sure you are aware of 
this when calling it.This is to avoid having
+two page views on a single call and to be compatible with old versions that 
did use the explicit call.</p>
+</div>
+<div class="paragraph">
+<p>By default the script will track page views, but maybe you want to take 
control over this mechanism of add page views
+to a single page application.In order to generate a page view programmatically 
from Javascript you can use code similar
+to this :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>    &lt;script type="text/javascript"&gt;
+        // This is an example of how to provide more details page properties 
to the view event. This can be useful
+        // in the case of an SPA that wants to provide information about a 
view that has metadata such as categories,
+        // tags or interests.
+        path = location.pathname + location.hash;
+        properties = {
+            path: path,
+            pageInfo: {
+                destinationURL: location.href,
+                tags : [ "tag1", "tag2", "tag3"],
+                categories : ["category1", "category2", "category3"],
+            },
+            interests : {
+                "interest1" : 1,
+                "interest2" : 2,
+                "interest3" : 3
+            }
+        };
+        console.log(properties);
+        // this will trigger a second page view for the same page (the first 
page view is in the tracker snippet).
+        window.unomiTracker.page(properties);
+    &lt;/script&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here is a more detail view of what you may include in the pageInfo object 
:</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<caption class="title">Table 1. PageInfo Properties</caption>
+<colgroup>
+<col style="width: 50%;">
+<col style="width: 50%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Name</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">pageID</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A unique 
identifier in string format for the page. Default value : page path</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">pageName</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A 
user-displayed name for the page. Default value : page title</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">pagePath</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The path 
of the page, stored by Unomi. This value should be the same as the one passed 
in the <code>page</code> property of the
+object passed to the unomiTracker call. Default value : page path</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">destinationURL</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The full 
URL for the page view. This doesn&#8217;t have to be a real existing URL it 
could be an internal SPA route. Default value : page URL</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">referringURL</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The 
referringURL also known as the previous URL of the page/screen viewed. Default 
value : page referrer URL</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">tags</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A String 
array of tag identifiers. For example <code>['tag1', 'tag2', 
'tag3']</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">categories</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A String 
array of category identifiers. For example <code>['category1', 'category2', 
'category3']</code></p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>The <code>interests</code> object is basically list of interests with 
"weights" attached to them.These interests will be accumulated
+in Apache Unomi on profiles to indicate growing interest over time for 
specific topics.These are freely defined and
+will be accepted by Apache Unomi without needing to declare them previously 
anywhere (the same is true for tags and
+categories).</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_tracking_form_submissions">2.4.4. Tracking form submissions</h4>
+<div class="paragraph">
+<p>Using the web tracker you can also track form submissions. In order to do 
this a few steps are required to get a form&#8217;s
+submission to be tracked and then its form values to be sent as events to 
Apache Unomi. Finally setting up a rule to
+react to the incoming event will help use the form values to perform any 
action that is desired.</p>
+</div>
+<div class="paragraph">
+<p>Let&#8217;s look at a concrete example. Before we get started you should 
know that this example is already available to
+directly test in Apache Unomi at the following URL :</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>http://localhost:8181/tracker</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Simply modify the form values and click submit and it will perform all the 
steps we are describing below.</p>
+</div>
+<div class="paragraph">
+<p>So here is the form we want to track :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>&lt;form id="testFormTracking" action="#" 
name="testFormTracking"&gt;
+    &lt;label for="firstName"&gt;First name&lt;/label&gt;
+    &lt;input type="text" id="firstName" name="firstName" value="John"/&gt;
+
+    &lt;label for="lastName"&gt;Last name&lt;/label&gt;
+    &lt;input type="text" id="lastName" name="lastName" value="Doe"/&gt;
+
+    &lt;label for="email"&gt;Email&lt;/label&gt;
+    &lt;input type="email" id="email" name="email" 
value="[email protected]"/&gt;
+
+    &lt;input type="submit" name="submitButton" value="Submit"/&gt;
+&lt;/form&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As you can see it&#8217;s composed of three fields - firstName, lastName 
and email - as well as a submit button. In order to
+track it we can add directly under the following snippet :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>&lt;script type="text/javascript"&gt;
+    window.addEventListener("load", function () {
+        var form = document.getElementById('testFormTracking');
+        unomiTracker.trackForm(form, 'formSubmitted', {formName: form.name});
+    });
+&lt;/script&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>What this snippet does is retrieve the form using its element ID and then 
uses the unomiTracker to track form submissions.
+Be careful to always use in the form event name a string that starts with 
<code>form</code> in order for the event to be sent back
+to Unomi. Also the form name is also a mandatory parameter that will be passed 
to Unomi inside a event of type <code>form</code> under
+the <code>target.itemId</code> property name.</p>
+</div>
+<div class="paragraph">
+<p>Here is an example of the event that gets sent back to Apache Unomi:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>{
+  "itemId" : "cd627012-963e-4bb5-97f0-480990b41254",
+  "itemType" : "event",
+  "scope" : "realEstateManager",
+  "version" : 1,
+  "eventType" : "form",
+  "sessionId" : "aaad09aa-88c2-67bd-b106-5a47ded43ead",
+  "profileId" : "48563fd0-6319-4260-8dba-ae421beba26f",
+  "timeStamp" : "2018-11-23T16:32:26Z",
+  "properties" : {
+    "firstName" : "John",
+    "lastName" : "Doe",
+    "email" : "[email protected]",
+    "submitButton" : "Submit"
+  },
+  "source" : {
+    "itemId" : "/tracker/",
+    "itemType" : "page",
+    "scope" : "realEstateManager",
+    "version" : null,
+    "properties" : {
+      "pageInfo" : {
+        "destinationURL" : 
"http://localhost:8181/tracker/?firstName=Bill&amp;lastName=Gates&amp;email=bgates%40microsoft.com";,
+        "pageID" : "/tracker/",
+        "pagePath" : "/tracker/",
+        "pageName" : "Apache Unomi Web Tracker Test Page",
+        "referringURL" : 
"http://localhost:8181/tracker/?firstName=John&amp;lastName=Doe&amp;email=johndoe%40acme.com";
+      },
+      "attributes" : [ ],
+      "consentTypes" : [ ],
+      "interests" : { }
+    }
+  },
+  "target" : {
+    "itemId" : "testFormTracking",
+    "itemType" : "form",
+    "scope" : "realEstateManager",
+    "version" : null,
+    "properties" : { }
+  },
+  "persistent" : true
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can see in this event that the form values are sent as properties of 
the event itself, while the form name is sent
+as the <code>target.itemId</code></p>
+</div>
+<div class="paragraph">
+<p>While setting up form tracking, it can be very useful to use the Apache 
Unomi Karaf SSH shell commands : <code>event-tail</code>
+and <code>event-view</code> to check if you are properly receiving the form 
submission events and that they contain the expected
+data. If not, check your tracking code for any errors.</p>
+</div>
+<div class="paragraph">
+<p>Now that the data is properly sent using an event to Apache Unomi, we must 
still use it to perform some kind of actions.
+Using rules, we could do anything from updating the profile to sending the 
data to a third-party server (using a custom-
+developped action of course). In this example we will illustrate how to update 
the profile.</p>
+</div>
+<div class="paragraph">
+<p>In order to do so we will deploy a rule that will copy data coming from the 
event into a profile. But we will need to
+map the form field names to profile names, and this can be done using the 
<code>setPropertyAction</code> that&#8217;s available out of the
+box in the Apache Unomi server.</p>
+</div>
+<div class="paragraph">
+<p>There are two ways to register rules : either by building a custom OSGi 
bundle plugin or using the REST API to directly
+send a JSON representation of the rule to be saved. We will in this example 
use the CURL shell command to make a call to
+the REST API.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST -k -u karaf:karaf 
https://localhost:9443/cxs/rules \
+  --header "Content-Type: application/json" \
+-d @- &lt;&lt; EOF
+{
+  "itemId": "form-mapping-example",
+  "itemType": "rule",
+  "linkedItems": null,
+  "raiseEventOnlyOnceForProfile": false,
+  "raiseEventOnlyOnceForSession": false,
+  "priority": -1,
+  "metadata": {
+    "id": "form-mapping-example",
+    "name": "Example Form Mapping",
+    "description": "An example of how to map event properties to profile 
properties",
+    "scope": "realEstateManager",
+    "tags": [],
+    "enabled": true,
+    "missingPlugins": false,
+    "hidden": false,
+    "readOnly": false
+  },
+  "condition": {
+    "type": "formEventCondition",
+    "parameterValues": {
+      "formId": "testFormTracking",
+      "pagePath" : "/tracker/"
+    }
+  },
+  "actions": [
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(firstName)",
+        "setPropertyValue": "eventProperty::properties(firstName)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    },
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(lastName)",
+        "setPropertyValue": "eventProperty::properties(lastName)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    },
+    {
+      "type": "setPropertyAction",
+      "parameterValues": {
+        "setPropertyName": "properties(email)",
+        "setPropertyValue": "eventProperty::properties(email)",
+        "setPropertyStrategy": "alwaysSet"
+      }
+    }
+  ]
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As you can see in this request, we have a few parameters that need 
explaining:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>-k</code> is used to accept any certificate as we are in this example 
using a default Apache Unomi server configuration that
+comes with its predefined HTTPS certificates</p>
+</li>
+<li>
+<p><code>-u karaf:karaf</code> is the default username/password for 
authenticating to the REST API. To change this value you should
+edit the `etc/users.properties`file and it is required to modify this login 
before going to production.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Finally the rule itself should be pretty self-explanatory but there are a 
few important things to note :</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>the <code>itemId</code> and <code>metadata.id</code> values should be the 
same</p>
+</li>
+<li>
+<p>the <code>scope</code> should be the same as the scope that was setup in 
the tracker initialization</p>
+</li>
+<li>
+<p>the <code>formId</code> parameter must have the form name value</p>
+</li>
+<li>
+<p>the <code>pagePath</code> should be the pagePath passed through the event 
(if you&#8217;re not sure of its value, you could either using
+network debugging in the browser or use the <code>event-tail</code> and 
<code>event-view</code> commands in the Apache Unomi Karaf SSH shell).</p>
+</li>
+<li>
+<p>the setPropertyAction may be repeated as many times as desired to copy the 
values from the event to the profile. Note that
+the <code>setPropertyName</code> will define the property to set on the 
profile and the <code>setPropertyValue</code> will define where the
+value is coming from. In this example the name and the value are the same but 
that is no way a requirement. It could
+even be possible to using multiple <code>setPropertyAction</code> instances to 
copy the same event property into different profile
+properties.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>To check if your rule is properly deployed you can use the following SSH 
shell command :</p>
+</div>
+<div class="paragraph">
+<p><code>unomi:rule-view form-mapping-example</code></p>
+</div>
+<div class="paragraph">
+<p>The parameter is the <code>itemId</code> of the rule. If you want to see 
all the rules deployed in the system you can use the
+command :</p>
+</div>
+<div class="paragraph">
+<p><code>unomi:rule-list 1000</code></p>
+</div>
+<div class="paragraph">
+<p>The <code>1000</code> parameter is the limit of number of objects to 
retrieve. As the number of rules can grow quickly in an Apache
+Unomi instance, it is recommended to put this value a bit high to make sure 
you get the full list of rules.</p>
+</div>
+<div class="paragraph">
+<p>Once the rule is in place, try submitting the form with some values and 
check that the profile is properly updated. One
+recommend way of doing this is to use the <code>event-tail</code> command that 
will output something like this :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>ID                                  |Type         
 |Session                             |Profile                             
|Timestamp                    |Scope          |Persi|
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+cef09b89-6b99-4e4f-a99c-a4159a66b42b|form          
|aaad09aa-88c2-67bd-b106-5a47ded43ead|48563fd0-6319-4260-8dba-ae421beba26f|Fri 
Nov 23 17:52:33 CET 2018 |realEstateManag|true |</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can directly see the profile that is being used, so you can then simply 
use the</p>
+</div>
+<div class="paragraph">
+<p><code>unomi:profile-view 48563fd0-6319-4260-8dba-ae421beba26f</code></p>
+</div>
+<div class="paragraph">
+<p>command to see a JSON dump of the profile and check that the form values 
have been properly positioned.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_configuration">2.5. Configuration</h3>
+<div class="sect3">
+<h4 id="_centralized_configuration">2.5.1. Centralized configuration</h4>
+<div class="paragraph">
+<p>Apache Unomi uses a centralized configuration file that contains both 
system properties and configuration properties.
+These settings are then fed to the OSGi and other configuration files using 
placeholder that look something like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre 
class="highlight"><code>contextserver.publicAddress=${org.apache.unomi.cluster.public.address:-http://localhost:8181}
+contextserver.internalAddress=${org.apache.unomi.cluster.internal.address:-https://localhost:9443}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Default values are stored in a file called 
<code>$MY_KARAF_HOME/etc/custom.system.properties</code> but you should never 
modify
+this file directly, as an override mechanism is available. Simply create a 
file called:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>unomi.custom.system.properties</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>and put your own property values in their to override the defaults OR you 
can use environment variables to also override
+the values in the <code>$MY_KARAF_HOME/etc/custom.system.properties</code>. 
See the next section for more information about that.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 
id="_changing_the_default_configuration_using_environment_variables_i_e_docker_configuration">2.5.2.
 Changing the default configuration using environment variables (i.e. Docker 
configuration)</h4>
+<div class="paragraph">
+<p>You might want to use environment variables to change the default system 
configuration, especially if you intend to run
+Apache Unomi inside a Docker container. You can find the list of all the 
environment variable names in the following file:</p>
+</div>
+<div class="paragraph">
+<p><a 
href="https://github.com/apache/unomi/blob/master/package/src/main/resources/etc/custom.system.properties";
 
class="bare">https://github.com/apache/unomi/blob/master/package/src/main/resources/etc/custom.system.properties</a></p>
+</div>
+<div class="paragraph">
+<p>If you are using Docker Container, simply pass the environment variables on 
the docker command line or if you are using
+Docker Compose you can put the environment variables in the docker-compose.yml 
file.</p>
+</div>
+<div class="paragraph">
+<p>If you want to "save" the environment values in a file, you can use the 
<code>bin/setenv(.bat)</code> to setup the environment
+variables you want to use.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_changing_the_default_configuration_using_property_files">2.5.3. 
Changing the default configuration using property files</h4>
+<div class="paragraph">
+<p>If you want to change the default configuration using property files 
instead of environment variables, you can perform
+any modification you want in the 
<code>$MY_KARAF_HOME/etc/unomi.custom.system.properties</code> file.</p>
+</div>
+<div class="paragraph">
+<p>By default this file does not exist and is designed to be a file that will 
contain only your custom modifications to the
+default configuration.</p>
+</div>
+<div class="paragraph">
+<p>For example, if you want to change the HTTP ports that the server is 
listening on, you will need to create the
+following lines in the $MY_KARAF_HOME/etc/unomi.custom.system.properties (and 
create it if you haven&#8217;t yet) file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>org.osgi.service.http.port.secure=9443
+org.osgi.service.http.port=8181</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If you change these ports, also make sure you adjust the following settings 
in the same file :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre 
class="highlight"><code>org.apache.unomi.cluster.public.address=http://localhost:8181
+org.apache.unomi.cluster.internal.address=https://localhost:9443</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If you need to specify an ElasticSearch cluster name, or a host and port 
that are different than the default,
+it is recommended to do this BEFORE you start the server for the first time, 
or you will loose all the data
+you have stored previously.</p>
+</div>
+<div class="paragraph">
+<p>You can use the following properties for the ElasticSearch configuration</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre 
class="highlight"><code>org.apache.unomi.elasticsearch.cluster.name=contextElasticSearch
+# The elasticsearch.adresses may be a comma seperated list of host names and 
ports such as
+# hostA:9200,hostB:9200
+# Note: the port number must be repeated for each host.
+org.apache.unomi.elasticsearch.addresses=localhost:9200</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_secured_events_configuration">2.5.4. Secured events configuration</h4>
+<div class="paragraph">
+<p>Apache Unomi secures some events by default. It comes out of the box with a 
default configuration that you can adjust
+by using the centralized configuration file override in 
<code>$MY_KARAF_HOME/etc/unomi.custom.system.properties</code></p>
+</div>
+<div class="paragraph">
+<p>You can find the default configuration in the following file:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>$MY_KARAF_HOME/etc/custom.system.properties</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The properties start with the prefix : 
<code>org.apache.unomi.thirdparty.*</code> and here are the default values :</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>org.apache.unomi.thirdparty.provider1.key=${env:UNOMI_THIRDPARTY_PROVIDER1_KEY:-670c26d1cc413346c3b2fd9ce65dab41}
+org.apache.unomi.thirdparty.provider1.ipAddresses=${env:UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES:-127.0.0.1,::1}
+org.apache.unomi.thirdparty.provider1.allowedEvents=${env:UNOMI_THIRDPARTY_PROVIDER1_ALLOWEDEVENTS:-login,updateProperties}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The events set in allowedEvents will be secured and will only be accepted 
if the call comes from the specified IP
+address, and if the secret-key is passed in the X-Unomi-Peer HTTP request 
header. The "env:" part means that it will
+attempt to read an environment variable by that name, and if it&#8217;s not 
found it will default to the value after the ":-"
+marker.</p>
+</div>
+<div class="paragraph">
+<p>It is now also possible to use IP address ranges instead of having to list 
all valid IP addresses for event sources. This
+is very useful when working in cluster deployments where servers may be added 
or removed dynamically. In order to support
+this Apache Unomi uses a library called <a 
href="https://seancfoley.github.io/IPAddress/#_Toc525135541";>IPAddress</a> that 
supports
+IP ranges and subnets. Here is an example of how to setup a range:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>org.apache.unomi.thirdparty.provider1.ipAddresses=${env:UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES:-192.168.1.1-100,::1}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The above configuration will allow a range of IP addresses between 
192.168.1.1 and 192.168.1.100 as well as the IPv6
+loopback.</p>
+</div>
+<div class="paragraph">
+<p>Here&#8217;s another example using the subnet format:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>org.apache.unomi.thirdparty.provider1.ipAddresses=${env:UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES:-1.2.0.0/16,::1}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The above configuration will allow all addresses starting with 1.2 as well 
as the IPv6 loopback address.</p>
+</div>
+<div class="paragraph">
+<p>Wildcards may also be used:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>org.apache.unomi.thirdparty.provider1.ipAddresses=${env:UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES:-1.2.*.*,::1}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The above configuration is exactly the same as the previous one.</p>
+</div>
+<div class="paragraph">
+<p>More advanced ranges and subnets can be used as well, please refer to the 
<a href="https://seancfoley.github.io/IPAddress";>IPAddress</a> library 
documentation for details on
+how to format them.</p>
+</div>
+<div class="paragraph">
+<p>If you want to add another provider you will need to add them manually in 
the following file (and make sure you maintain
+the changes when upgrading) :</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>$MY_KARAF_HOME/etc/org.apache.unomi.thirdparty.cfg</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Usually, login events, which operate on profiles and do merge on protected 
properties, must be secured. For each
+trusted third party server, you need to add these 3 lines :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>thirdparty.provider1.key=secret-key
+thirdparty.provider1.ipAddresses=127.0.0.1,::1
+thirdparty.provider1.allowedEvents=login,updateProperties</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_installing_the_maxmind_geoiplite2_ip_lookup_database">2.5.5. 
Installing the MaxMind GeoIPLite2 IP lookup database</h4>
+<div class="paragraph">
+<p>Apache Unomi requires an IP database in order to resolve IP addresses to 
user location.
+The GeoLite2 database can be downloaded from MaxMind here :
+<a 
href="http://dev.maxmind.com/geoip/geoip2/geolite2/";>http://dev.maxmind.com/geoip/geoip2/geolite2/</a></p>
+</div>
+<div class="paragraph">
+<p>Simply download the GeoLite2-City.mmdb file into the "etc" directory.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_installing_geonames_database">2.5.6. Installing Geonames database</h4>
+<div class="paragraph">
+<p>Apache Unomi includes a geocoding service based on the geonames database ( 
<a href="http://www.geonames.org/";>http://www.geonames.org/</a> ). It can be
+used to create conditions on countries or cities.</p>
+</div>
+<div class="paragraph">
+<p>In order to use it, you need to install the Geonames database into . Get 
the "allCountries.zip" database from here :
+<a 
href="http://download.geonames.org/export/dump/";>http://download.geonames.org/export/dump/</a></p>
+</div>
+<div class="paragraph">
+<p>Download it and put it in the "etc" directory, without unzipping it.
+Edit <code>$MY_KARAF_HOME/etc/unomi.custom.system.properties</code> and set 
<code>org.apache.unomi.geonames.forceImport</code> to true,
+import should start right away.
+Otherwise, import should start at the next startup. Import runs in background, 
but can take about 15 minutes.
+At the end, you should have about 4 million entries in the geonames index.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_rest_api_security">2.5.7. REST API Security</h4>
+<div class="paragraph">
+<p>The Apache Unomi Context Server REST API is protected using JAAS 
authentication and using Basic or Digest HTTP auth.
+By default, the login/password for the REST API full administrative access is 
"karaf/karaf".</p>
+</div>
+<div class="paragraph">
+<p>The generated package is also configured with a default SSL certificate. 
You can change it by following these steps :</p>
+</div>
+<div class="paragraph">
+<p>Replace the existing keystore in $MY_KARAF_HOME/etc/keystore by your own 
certificate :</p>
+</div>
+<div class="paragraph">
+<p><a 
href="http://wiki.eclipse.org/Jetty/Howto/Configure_SSL";>http://wiki.eclipse.org/Jetty/Howto/Configure_SSL</a></p>
+</div>
+<div class="paragraph">
+<p>Update the keystore and certificate password in 
$MY_KARAF_HOME/etc/unomi.custom.system.properties file :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre 
class="highlight"><code>org.ops4j.pax.web.ssl.keystore=${env:UNOMI_SSL_KEYSTORE:-${karaf.etc}/keystore}
+org.ops4j.pax.web.ssl.password=${env:UNOMI_SSL_PASSWORD:-changeme}
+org.ops4j.pax.web.ssl.keypassword=${env:UNOMI_SSL_KEYPASSWORD:-changeme}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You should now have SSL setup on Karaf with your certificate, and you can 
test it by trying to access it on port 9443.</p>
+</div>
+<div class="paragraph">
+<p>Changing the default Karaf password can be done by modifying the 
<code>org.apache.unomi.security.root.password</code> in the
+<code>$MY_KARAF_HOME/etc/unomi.custom.system.properties</code> file</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_scripting_security">2.5.8. Scripting security</h4>
+<div class="paragraph">
+<p>By default, scripting (using in conditions, segments and rules) is 
controlled by a custom classloader that is quite
+restrictive and using a white-list/black list system. It is controlled through 
the following property in the
+<code>unomi.custom.system.properties</code> file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre 
class="highlight"><code>org.apache.unomi.scripting.allow=${env:UNOMI_ALLOW_SCRIPTING_CLASSES:-org.apache.unomi.api.Event,org.apache.unomi.api.Profile,org.apache.unomi.api.Session,org.apache.unomi.api.Item,org.apache.unomi.api.CustomItem,ognl.*,java.lang.Object,java.util.Map,java.lang.Integer,org.mvel2.*}
+org.apache.unomi.scripting.forbid=${env:UNOMI_FORBID_SCRIPTING_CLASSES:-}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If you encounter any errors while trying to access a class in a condition 
or an action it might be due to this
+restrictive configuration.</p>
+</div>
+<div class="paragraph">
+<p>If you need, for example when adding a custom item type, to adjust these, 
please be careful as scripts may be called
+directly from the context.json personalization conditions and therefore should 
be kept minimal.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_groovy_actions">2.5.9. Groovy Actions</h4>
+<div class="paragraph">
+<p>Groovy actions offer the ability to define a set of actions and action 
types (aka action descriptors) purely from Groovy scripts defined at 
runtime.</p>
+</div>
+<div class="paragraph">
+<p>Initially submitted to Unomi through a purpose-built REST API endpoint, 
Groovy actions are then stored in Elasticsearch. When an event matches a rule 
configured to execute an action, the corresponding action is fetched from 
Elasticsearch and executed.</p>
+</div>
+<div class="sect4">
+<h5 id="_anatomy_of_a_groovy_action">Anatomy of a Groovy Action</h5>
+<div class="paragraph">
+<p>To be valid, a Groovy action must follow a particular convention which is 
divided in two parts:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>An annotation used to define the associated action type</p>
+</li>
+<li>
+<p>The function to be executed</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Placed right before the function, the “@Action” annotation contains a 
set of parameter detailing how the action should be triggered.</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<caption class="title">Table 2. @Action annotation</caption>
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Field name</th>
+<th class="tableblock halign-left valign-top">Type</th>
+<th class="tableblock halign-left valign-top">Required</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">id</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">String</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">YES</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Id of the 
action</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">actionExecutor</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">String</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">YES</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Action 
executor contains the name of the script to call for the action type and must 
be prefixed with “<strong>groovy:</strong>”. The prefix indicates to Unomi 
which dispatcher to use when processing the action.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">name</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">String</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Action 
name</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">hidden</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">Boolean</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Define if 
the action is hidden or not. It is usually used to hide objects in a 
UI.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">parameters</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">List&lt;<a 
href="https://github.com/apache/unomi/blob/master/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/annotations/Parameter.java";>Parameter</a>&gt;</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The 
parameters of the actions, also defined by annotations</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">systemTags</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">List&lt;String&gt;</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A 
(reserved) list of tags for the associated object. This is usually populated 
through JSON descriptors and is not meant to be modified by end users. These 
tags may include values that help classify associated objects.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>The function contained within the Groovy Action must be called 
<code>execute()</code> and its last instruction must be an integer.</p>
+</div>
+<div class="paragraph">
+<p>This integer serves as an indication whether the values of the session and 
profile should be persisted. In general, the codes used are defined in the <a 
href="https://github.com/apache/unomi/blob/master/api/src/main/java/org/apache/unomi/api/services/EventService.java";>EventService
 interface</a>.</p>
+</div>
+<div class="paragraph">
+<p>Each groovy actions extends by default a Base script
+<a 
href="https://github.com/apache/unomi/blob/master/extensions/groovy-actions/services/src/main/resources/META-INF/base/BaseScript.groovy";>defined
 here</a></p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_rest_api">REST API</h5>
+<div class="paragraph">
+<p>Actions can be deployed/updated/deleted via the dedicated 
<code>/cxs/groovyActions</code> rest endpoint.</p>
+</div>
+<div class="paragraph">
+<p>Deploy/update an Action:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-bash" data-lang="bash">curl -X 
POST 'http://localhost:8181/cxs/groovyActions' \
+--user karaf:karaf \
+--form 'file=@"&lt;file location&gt;"'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>A Groovy Action can be updated by submitting another Action with the same 
id.</p>
+</div>
+<div class="paragraph">
+<p>Delete an Action:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-bash" data-lang="bash">curl -X 
DELETE 'http://localhost:8181/cxs/groovyActions/&lt;Action id&gt;' \
+--user karaf:karaf</code></pre>
+</div>
+</div>

[... 7194 lines stripped ...]


Reply via email to