You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@unomi.apache.org by sh...@apache.org on 2020/02/17 14:52:57 UTC

svn commit: r1874137 [2/4] - in /unomi/website/manual: 1_1_x/ 1_2_x/ 1_3_x/ 1_4_x/ 1_4_x/connectors/ 1_4_x/images/ 1_4_x/samples/ latest/ latest/images/

Added: unomi/website/manual/1_4_x/index.html
URL: http://svn.apache.org/viewvc/unomi/website/manual/1_4_x/index.html?rev=1874137&view=auto
==============================================================================
--- unomi/website/manual/1_4_x/index.html (added)
+++ unomi/website/manual/1_4_x/index.html Mon Feb 17 14:52:56 2020
@@ -0,0 +1,6152 @@
+<!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="#_concepts">2. Concepts</a>
+<ul class="sectlevel2">
+<li><a href="#_items_and_types">2.1. Items and types</a></li>
+<li><a href="#_events">2.2. Events</a></li>
+<li><a href="#_profiles">2.3. Profiles</a></li>
+<li><a href="#_sessions">2.4. Sessions</a></li>
+<li><a href="#_segments">2.5. Segments</a></li>
+<li><a href="#_conditions">2.6. Conditions</a></li>
+<li><a href="#_rules">2.7. Rules</a>
+<ul class="sectlevel3">
+<li><a href="#_actions">2.7.1. Actions</a></li>
+</ul>
+</li>
+<li><a href="#_request_flow">2.8. Request flow</a></li>
+</ul>
+</li>
+<li><a href="#_first_steps_with_apache_unomi">3. First steps with Apache Unomi</a>
+<ul class="sectlevel2">
+<li><a href="#_getting_started_with_unomi">3.1. Getting started with Unomi</a>
+<ul class="sectlevel3">
+<li><a href="#_prerequisites">3.1.1. Prerequisites</a></li>
+<li><a href="#_running_unomi">3.1.2. Running Unomi</a></li>
+</ul>
+</li>
+<li><a href="#_recipes">3.2. Recipes</a>
+<ul class="sectlevel3">
+<li><a href="#_introduction">3.2.1. Introduction</a></li>
+<li><a href="#_how_to_read_a_profile">3.2.2. How to read a profile</a></li>
+<li><a href="#_how_to_update_a_profile_from_the_public_internet">3.2.3. How to update a profile from the public internet</a></li>
+<li><a href="#_how_to_search_for_profile_events">3.2.4. How to search for profile events</a></li>
+<li><a href="#_how_to_create_a_new_rule">3.2.5. How to create a new rule</a></li>
+<li><a href="#_how_to_search_for_profiles">3.2.6. How to search for profiles</a></li>
+<li><a href="#_getting_updating_consents">3.2.7. Getting / updating consents</a></li>
+<li><a href="#_how_to_send_a_login_event_to_unomi">3.2.8. How to send a login event to Unomi</a></li>
+</ul>
+</li>
+<li><a href="#_request_examples">3.3. Request examples</a>
+<ul class="sectlevel3">
+<li><a href="#_retrieving_your_first_context">3.3.1. Retrieving your first context</a></li>
+<li><a href="#_retrieving_a_context_as_a_json_object">3.3.2. Retrieving a context as a JSON object.</a></li>
+<li><a href="#_accessing_profile_properties_in_a_context">3.3.3. Accessing profile properties in a context</a></li>
+<li><a href="#_sending_events_using_the_context_servlet">3.3.4. Sending events using the context servlet</a></li>
+<li><a href="#_sending_events_using_the_eventcollector_servlet">3.3.5. Sending events using the eventcollector servlet</a></li>
+<li><a href="#_where_to_go_from_here">3.3.6. Where to go from here</a></li>
+</ul>
+</li>
+<li><a href="#_web_tracker">3.4. Web Tracker</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started">3.4.1. Getting started</a></li>
+<li><a href="#_how_to_contribute">3.4.2. How to contribute</a></li>
+<li><a href="#_tracking_page_views">3.4.3. Tracking page views</a></li>
+<li><a href="#_tracking_form_submissions">3.4.4. Tracking form submissions</a></li>
+</ul>
+</li>
+<li><a href="#_configuration">3.5. Configuration</a>
+<ul class="sectlevel3">
+<li><a href="#_centralized_configuration">3.5.1. Centralized configuration</a></li>
+<li><a href="#_changing_the_default_configuration_using_environment_variables_i_e_docker_configuration">3.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">3.5.3. Changing the default configuration using property files</a></li>
+<li><a href="#_secured_events_configuration">3.5.4. Secured events configuration</a></li>
+<li><a href="#_installing_the_maxmind_geoiplite2_ip_lookup_database">3.5.5. Installing the MaxMind GeoIPLite2 IP lookup database</a></li>
+<li><a href="#_installing_geonames_database">3.5.6. Installing Geonames database</a></li>
+<li><a href="#_rest_api_security">3.5.7. REST API Security</a></li>
+<li><a href="#_automatic_profile_merging">3.5.8. Automatic profile merging</a></li>
+<li><a href="#_securing_a_production_environment">3.5.9. Securing a production environment</a></li>
+<li><a href="#_integrating_with_an_apache_http_web_server">3.5.10. Integrating with an Apache HTTP web server</a></li>
+<li><a href="#_changing_the_default_tracking_location">3.5.11. Changing the default tracking location</a></li>
+<li><a href="#_apache_karaf_ssh_console">3.5.12. Apache Karaf SSH Console</a></li>
+<li><a href="#_elasticsearch_x_pack_support">3.5.13. ElasticSearch X-Pack Support</a></li>
+</ul>
+</li>
+<li><a href="#_useful_apache_unomi_urls">3.6. Useful Apache Unomi URLs</a></li>
+<li><a href="#_how_profile_tracking_works">3.7. How profile tracking works</a>
+<ul class="sectlevel3">
+<li><a href="#_steps">3.7.1. Steps</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_queries_and_aggregations">4. Queries and aggregations</a>
+<ul class="sectlevel2">
+<li><a href="#_query_counts">4.1. Query counts</a></li>
+<li><a href="#_metrics">4.2. Metrics</a></li>
+<li><a href="#_aggregations">4.3. Aggregations</a>
+<ul class="sectlevel3">
+<li><a href="#_aggregation_types">4.3.1. Aggregation types</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_profile_import_export">5. Profile import &amp; export</a>
+<ul class="sectlevel2">
+<li><a href="#_importing_profiles">5.1. Importing profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_import_api">5.1.1. Import API</a></li>
+</ul>
+</li>
+<li><a href="#_exporting_profiles">5.2. Exporting profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_export_api">5.2.1. Export API</a></li>
+</ul>
+</li>
+<li><a href="#_configuration_in_details">5.3. Configuration in details</a></li>
+</ul>
+</li>
+<li><a href="#_consent_management">6. Consent management</a>
+<ul class="sectlevel2">
+<li><a href="#_consent_api">6.1. Consent API</a>
+<ul class="sectlevel3">
+<li><a href="#_profiles_with_consents">6.1.1. Profiles with consents</a></li>
+<li><a href="#_consent_type_definitions">6.1.2. Consent type definitions</a></li>
+<li><a href="#_creating_update_a_visitor_consent">6.1.3. Creating / update a visitor consent</a></li>
+<li><a href="#_how_it_works_internally">6.1.4. How it works (internally)</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_privacy_management">7. Privacy management</a>
+<ul class="sectlevel3">
+<li><a href="#_setting_up_access_to_the_privacy_endpoint">7.1. Setting up access to the privacy endpoint</a></li>
+<li><a href="#_anonymizing_a_profile">7.2. Anonymizing a profile</a></li>
+<li><a href="#_downloading_profile_data">7.3. Downloading profile data</a></li>
+<li><a href="#_deleting_a_profile">7.4. Deleting a profile</a></li>
+<li><a href="#_related">7.5. Related</a></li>
+</ul>
+</li>
+<li><a href="#_cluster_setup">8. Cluster setup</a>
+<ul class="sectlevel2">
+<li><a href="#_cluster_setup_2">8.1. Cluster setup</a></li>
+</ul>
+</li>
+<li><a href="#_reference">9. Reference</a>
+<ul class="sectlevel2">
+<li><a href="#_built_in_conditions">9.1. Built-in conditions</a>
+<ul class="sectlevel3">
+<li><a href="#_existing_condition_descriptors">9.1.1. Existing condition descriptors</a></li>
+</ul>
+</li>
+<li><a href="#_built_in_actions">9.2. Built-in actions</a>
+<ul class="sectlevel3">
+<li><a href="#_existing_actions_descriptors">9.2.1. Existing actions descriptors</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_integration_samples">10. Integration samples</a>
+<ul class="sectlevel2">
+<li><a href="#_samples">10.1. Samples</a></li>
+<li><a href="#_login_sample">10.2. Login sample</a>
+<ul class="sectlevel3">
+<li><a href="#_warning">10.2.1. Warning !</a></li>
+<li><a href="#_installing_the_samples">10.2.2. Installing the samples</a></li>
+</ul>
+</li>
+<li><a href="#_twitter_sample">10.3. Twitter sample</a>
+<ul class="sectlevel3">
+<li><a href="#_overview">10.3.1. Overview</a></li>
+<li><a href="#_interacting_with_the_context_server">10.3.2. Interacting with the context server</a></li>
+<li><a href="#_retrieving_context_information_from_unomi_using_the_context_servlet">10.3.3. Retrieving context information from Unomi using the context servlet</a></li>
+</ul>
+</li>
+<li><a href="#_example">10.4. Example</a>
+<ul class="sectlevel3">
+<li><a href="#_html_page">10.4.1. HTML page</a></li>
+<li><a href="#_javascript">10.4.2. Javascript</a></li>
+</ul>
+</li>
+<li><a href="#_conclusion">10.5. Conclusion</a></li>
+<li><a href="#_annex">10.6. Annex</a></li>
+<li><a href="#_weather_update_sample">10.7. Weather update sample</a></li>
+</ul>
+</li>
+<li><a href="#_connectors">11. Connectors</a>
+<ul class="sectlevel2">
+<li><a href="#_connectors_2">11.1. Connectors</a>
+<ul class="sectlevel3">
+<li><a href="#_call_for_contributors">11.1.1. Call for contributors</a></li>
+</ul>
+</li>
+<li><a href="#_salesforce_connector">11.2. Salesforce Connector</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started_2">11.2.1. Getting started</a></li>
+<li><a href="#_properties">11.2.2. Properties</a></li>
+<li><a href="#_hot_deploying_updates_to_the_salesforce_connector_for_developers">11.2.3. Hot-deploying updates to the Salesforce connector (for developers)</a></li>
+<li><a href="#_using_the_salesforce_workbench_for_testing_rest_api">11.2.4. Using the Salesforce Workbench for testing REST API</a></li>
+<li><a href="#_setting_up_streaming_push_queries">11.2.5. Setting up Streaming Push queries</a></li>
+<li><a href="#_executing_the_unit_tests">11.2.6. Executing the unit tests</a></li>
+</ul>
+</li>
+<li><a href="#_mailchimp_connector">11.3. MailChimp Connector</a>
+<ul class="sectlevel3">
+<li><a href="#_getting_started_3">11.3.1. Getting started</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_developers">12. Developers</a>
+<ul class="sectlevel2">
+<li><a href="#_building">12.1. Building</a>
+<ul class="sectlevel3">
+<li><a href="#_initial_setup">12.1.1. Initial Setup</a></li>
+<li><a href="#_building_2">12.1.2. Building</a></li>
+<li><a href="#_installing_an_elasticsearch_server">12.1.3. Installing an ElasticSearch server</a></li>
+<li><a href="#_deploying_the_generated_binary_package">12.1.4. Deploying the generated binary package</a></li>
+<li><a href="#_deploying_into_an_existing_karaf_server">12.1.5. Deploying into an existing Karaf server</a></li>
+<li><a href="#_jdk_selection_on_mac_os_x">12.1.6. JDK Selection on Mac OS X</a></li>
+<li><a href="#_running_the_integration_tests">12.1.7. Running the integration tests</a></li>
+<li><a href="#_running_the_performance_tests">12.1.8. Running the performance tests</a></li>
+<li><a href="#_testing_with_an_example_page">12.1.9. Testing with an example page</a></li>
+</ul>
+</li>
+<li><a href="#_ssh_shell_commands">12.2. SSH Shell Commands</a>
+<ul class="sectlevel3">
+<li><a href="#_using_the_shell">12.2.1. Using the shell</a></li>
+<li><a href="#_lifecycle_commands">12.2.2. Lifecycle commands</a></li>
+<li><a href="#_runtime_commands">12.2.3. Runtime commands</a></li>
+</ul>
+</li>
+<li><a href="#_types_vs_instances">12.3. Types vs. instances</a></li>
+<li><a href="#_plugin_structure">12.4. Plugin structure</a></li>
+<li><a href="#_extension_points">12.5. Extension points</a>
+<ul class="sectlevel3">
+<li><a href="#_actiontype">12.5.1. ActionType</a></li>
+<li><a href="#_conditiontype">12.5.2. ConditionType</a></li>
+<li><a href="#_persona">12.5.3. Persona</a></li>
+<li><a href="#_propertymergestrategytype">12.5.4. PropertyMergeStrategyType</a></li>
+<li><a href="#_propertytype">12.5.5. PropertyType</a></li>
+<li><a href="#_rule">12.5.6. Rule</a></li>
+<li><a href="#_scoring">12.5.7. Scoring</a></li>
+<li><a href="#_segments_2">12.5.8. Segments</a></li>
+<li><a href="#_tag">12.5.9. Tag</a></li>
+<li><a href="#_valuetype">12.5.10. ValueType</a></li>
+</ul>
+</li>
+<li><a href="#_other_unomi_entities">12.6. Other Unomi entities</a>
+<ul class="sectlevel3">
+<li><a href="#_userlist">12.6.1. UserList</a></li>
+<li><a href="#_goal">12.6.2. Goal</a></li>
+<li><a href="#_campaign">12.6.3. Campaign</a></li>
+</ul>
+</li>
+<li><a href="#_custom_extensions">12.7. Custom extensions</a>
+<ul class="sectlevel3">
+<li><a href="#_creating_an_extension">12.7.1. Creating an extension</a></li>
+<li><a href="#_deployment_and_custom_definition">12.7.2. Deployment and custom definition</a></li>
+<li><a href="#_predefined_segments">12.7.3. Predefined segments</a></li>
+<li><a href="#_predefined_rules">12.7.4. Predefined rules</a></li>
+<li><a href="#_predefined_properties">12.7.5. Predefined properties</a></li>
+<li><a href="#_predefined_child_conditions">12.7.6. Predefined child conditions</a></li>
+<li><a href="#_predefined_personas">12.7.7. Predefined personas</a></li>
+<li><a href="#_custom_actions">12.7.8. Custom actions</a></li>
+<li><a href="#_custom_conditions">12.7.9. Custom conditions</a></li>
+</ul>
+</li>
+<li><a href="#_migration_patches">12.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="http://www.oracle.com/technetwork/java/javase/downloads/index.html" class="bare">http://www.oracle.com/technetwork/java/javase/downloads/index.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></p>
+</div>
+<div class="paragraph">
+<p>2) Download ElasticSearch here : <a href="https://www.elastic.co/downloads/past-releases/elasticsearch-5-6-3" class="bare">https://www.elastic.co/downloads/past-releases/elasticsearch-5-6-3</a> (please &lt;strong&gt;make sure&lt;/strong&gt; you use the proper version : 5.6.3)</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>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_concepts">2. Concepts</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Apache Unomi gathers information about users actions, information that is processed and stored by Unomi services. The
+collected information can then be used to personalize content, derive insights on user behavior, categorize the user
+profiles into segments along user-definable dimensions or acted upon by algorithms.</p>
+</div>
+<div class="sect2">
+<h3 id="_items_and_types">2.1. Items and types</h3>
+<div class="paragraph">
+<p>Unomi structures the information it collects using the concept of <code>Item</code> which provides the base information (an
+identifier and a type) the context server needs to process and store the data. Items are persisted according to their
+type (structure) and identifier (identity). This base structure can be extended, if needed, using properties in the
+form of key-value pairs.</p>
+</div>
+<div class="paragraph">
+<p>These properties are further defined by the <code>Item</code>’s type definition which explicits the <code>Item</code>’s structure and
+semantics. By defining new types, users specify which properties (including the type of values they accept) are
+available to items of that specific type.</p>
+</div>
+<div class="paragraph">
+<p>Unomi defines default value types: <code>date</code>, <code>email</code>, <code>integer</code> and <code>string</code>, all pretty self-explanatory. While you can
+think of these value types as "primitive" types, it is possible to extend Unomi by providing additional value types.</p>
+</div>
+<div class="paragraph">
+<p>Additionally, most items are also associated to a scope, which is a concept that Unomi uses to group together related
+items. A given scope is represented in Unomi by a simple string identifier and usually represents an application or set
+of applications from which Unomi gathers data, depending on the desired analysis granularity. In the context of web
+sites, a scope could, for example, represent a site or family of related sites being analyzed. Scopes allow clients
+accessing the context server to filter data to only see relevant data.</p>
+</div>
+<div class="paragraph">
+<p><em>Base <code>Item</code> structure:</em></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-json" data-lang="json">{
+  "itemType": &lt;type of the item&gt;,
+  "scope": &lt;scope&gt;,
+  "itemId": &lt;item identifier&gt;,
+  "properties": &lt;optional properties&gt;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Some types can be dynamically defined at runtime by calling to the REST API while other extensions are done via Unomi
+plugins. Part of extending Unomi, therefore, is a matter of defining new types and specifying which kind of Unomi
+entity (e.g. profiles) they can be affected to. For example, the following JSON document can be passed to Unomi to
+declare a new property type identified (and named) <code>tweetNb</code>, tagged with the <code>social</code> tag, targeting profiles and
+using the <code>integer</code> value type.</p>
+</div>
+<div class="paragraph">
+<p><em>Example JSON type definition:</em></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-json" data-lang="json">{
+    "itemId": "tweetNb",
+    "itemType": "propertyType",
+    "metadata": {
+        "id": "tweetNb",
+        "name": "tweetNb",
+        "systemTags": ["social"]
+    },
+    "target": "profiles",
+    "type": "integer"
+}</code></pre>
+</div>
+</div>
+<div class="quoteblock">
+<blockquote>
+<div class="paragraph">
+<p>Unomi defines a built-in scope (called <code>systemscope</code>) that clients can use to share data across scopes.</p>
+</div>
+</blockquote>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_events">2.2. Events</h3>
+<div class="paragraph">
+<p>Users' actions are conveyed from clients to the context server using events. Of course, the required information
+depends on what is collected and users' interactions with the observed systems but events minimally provide a type, a
+scope and source and target items. Additionally, events are timestamped. Conceptually, an event can be seen as a
+sentence, the event&#8217;s type being the verb, the source the subject and the target the object.</p>
+</div>
+<div class="paragraph">
+<p><em>Event structure:</em></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-json" data-lang="json">{
+    "eventType": &lt;type of the event&gt;,
+    "scope": &lt;scope of the event&gt;,
+    "source": &lt;Item&gt;,
+    "target": &lt;Item&gt;,
+    "properties": &lt;optional properties&gt;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Source and target can be any Unomi item but are not limited to them. In particular, as long as they can be described
+using properties and Unomi’s type mechanism and can be processed either natively or via extension plugins, source and
+target can represent just about anything. Events can also be triggered as part of Unomi’s internal processes for example
+when a rule is triggered.</p>
+</div>
+<div class="paragraph">
+<p>Events are sent to Unomi from client applications using the JSON format and a typical page view event from a web site
+could look something like the following:</p>
+</div>
+<div class="paragraph">
+<p><em>Example page view event:</em></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-json" data-lang="json">{
+    "eventType": "view",
+    "scope": "ACMESPACE",
+    "source": {
+        "itemType": "site",
+        "scope": "ACMESPACE",
+        "itemId": "c4761bbf-d85d-432b-8a94-37e866410375"
+    },
+    "target": {
+        "itemType": "page",
+        "scope": "ACMESPACE",
+        "itemId": "b6acc7b3-6b9d-4a9f-af98-54800ec13a71",
+        "properties": {
+            "pageInfo": {
+            "pageID": "b6acc7b3-6b9d-4a9f-af98-54800ec13a71",
+            "pageName": "Home",
+            "pagePath": "/sites/ACMESPACE/home",
+            "destinationURL": "http://localhost:8080/sites/ACMESPACE/home.html",
+            "referringURL": "http://localhost:8080/",
+            "language": "en"
+        },
+        "category": {},
+        "attributes": {}
+      }
+    }
+}</code></pre>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_profiles">2.3. Profiles</h3>
+<div class="paragraph">
+<p>By processing events, Unomi progressively builds a picture of who the user is and how they behave. This knowledge is
+embedded in <code>Profile</code> object. A profile is an <code>Item</code> with any number of properties and optional segments and scores.
+Unomi provides default properties to cover common data (name, last name, age, email, etc.) as well as default segments
+to categorize users. Unomi users are, however, free and even encouraged to create additional properties and segments to
+better suit their needs.</p>
+</div>
+<div class="paragraph">
+<p>Contrary to other Unomi items, profiles are not part of a scope since we want to be able to track the associated user
+across applications. For this reason, data collected for a given profile in a specific scope is still available to any
+scoped item that accesses the profile information.</p>
+</div>
+<div class="paragraph">
+<p>It is interesting to note that there is not necessarily a one to one mapping between users and profiles as users can be
+captured across applications and different observation contexts. As identifying information might not be available in
+all contexts in which data is collected, resolving profiles to a single physical user can become complex because
+physical users are not observed directly. Rather, their portrait is progressively patched together and made clearer as
+Unomi captures more and more traces of their actions. Unomi will merge related profiles as soon as collected data
+permits positive association between distinct profiles, usually as a result of the user performing some identifying
+action in a context where the user hadn’t already been positively identified.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_sessions">2.4. Sessions</h3>
+<div class="paragraph">
+<p>A session represents a time-bounded interaction between a user (via their associated profile) and a Unomi-enabled
+application. A session represents the sequence of actions the user performed during its duration. For this reason,
+events are associated with the session during which they occurred. In the context of web applications, sessions are
+usually linked to HTTP sessions.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_segments">2.5. Segments</h3>
+<div class="paragraph">
+<p>Segments are used to group profiles together, and are based on conditions that are executed on profiles to determine if
+they are part of a segment or not. This also means that a profile may enter or leave a segment based on changes in their
+properties, making segments a highly dynamic concept.</p>
+</div>
+<div class="paragraph">
+<p>Here is an example of a simple segment definition registered using the REST API:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/segments \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+-d @- &lt;&lt;'EOF'
+{
+  "metadata": {
+    "id": "leads",
+    "name": "Leads",
+    "scope": "systemscope",
+    "description": "You can customize the list below by editing the leads segment.",
+    "readOnly":true
+  },
+  "condition": {
+    "type": "booleanCondition",
+    "parameterValues": {
+      "operator" : "and",
+      "subConditions": [
+        {
+          "type": "profilePropertyCondition",
+          "parameterValues": {
+            "propertyName": "properties.leadAssignedTo",
+            "comparisonOperator": "exists"
+          }
+        }
+      ]
+    }
+  }
+}
+EOF</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For more details on the conditions and how they are structured using conditions, see the next section.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_conditions">2.6. Conditions</h3>
+<div class="paragraph">
+<p>Conditions are a very useful notion inside of Apache Unomi, as they are used as the basis for multiple other objects.
+Conditions may be used as parts of:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Segments</p>
+</li>
+<li>
+<p>Rules</p>
+</li>
+<li>
+<p>Queries</p>
+</li>
+<li>
+<p>Campaigns</p>
+</li>
+<li>
+<p>Goals</p>
+</li>
+<li>
+<p>Profile filters</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>A condition is composed of two basic elements:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>a condition type identifier</p>
+</li>
+<li>
+<p>a list of parameter values for the condition, that can be of any type, and in some cases may include sub-conditions</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>A condition type identifier is a string that contains a unique identifier for a condition type. Example condition types
+may include <code>booleanCondition</code>, <code>eventTypeCondition</code>, <code>eventPropertyCondition</code>, and so on. Plugins may implement new
+condition types that may implement any logic that may be needed. The parameter values are simply lists of objects that
+may be used to configure the condition. In the case of a <code>booleanCondition</code> for example one of the parameter values will
+be an <code>operator</code> that will contain values such as <code>and</code> or <code>or</code> and a second parameter value called <code>subConditions</code>
+that contains a list of conditions to evaluate with that operator. The result of a condition is always a boolean
+value of true or false.</p>
+</div>
+<div class="paragraph">
+<p>Apache Unomi provides quite a lot of built-in condition types, including boolean types that make it possible to
+compose conditions using operators such as <code>and</code>, <code>or</code> or <code>not</code>. Composition is an essential element of building more
+complex conditions.</p>
+</div>
+<div class="paragraph">
+<p>Here is an example of a complex condition:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-json" data-lang="json">{
+  "condition": {
+    "type": "booleanCondition",
+    "parameterValues": {
+      "operator":"or",
+      "subConditions":[
+        {
+          "type": "eventTypeCondition",
+          "parameterValues": {
+            "eventTypeId": "sessionCreated"
+          }
+        },
+        {
+          "type": "eventTypeCondition",
+          "parameterValues": {
+            "eventTypeId": "sessionReassigned"
+          }
+        }
+      ]
+    }
+  }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As we can see in the above example we use the boolean <code>or</code> condition to check if the event type is of type <code>sessionCreated</code>
+or <code>sessionReassigned</code>.</p>
+</div>
+<div class="paragraph">
+<p>For a more complete list of available conditions, see the <a href="#_built_in_conditions">Built-in conditions</a> reference section.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_rules">2.7. Rules</h3>
+<div class="imageblock">
+<div class="content">
+<img src="images/unomi-rule-engine.png" alt="Unomi Rule Engine">
+</div>
+</div>
+<div class="paragraph">
+<p>Apache Unomi has a built-in rule engine that is one of the most important components of its architecture. Every time
+an event is received by the server, it is evaluated against all the rules and the ones matching the incoming event will
+be executed. You can think of a rule as a structure that looks like this:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>when
+    conditions
+then
+    actions</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Basically when a rule is evaluated, all the conditions in the <code>when</code> part are evaluated and if the result matches
+(meaning it evaluates to <code>true</code>) then the actions will be executed in sequence.</p>
+</div>
+<div class="paragraph">
+<p>The real power of Apache Unomi comes from the fact that <code>conditions</code> and <code>actions</code> are fully pluggeable and that plugins
+may implement new conditions and/or actions to perform any task. You can imagine conditions checking incoming event data
+against third-party systems or even against authentication systesm, and actions actually pulling or pushing data to third-party
+systems.</p>
+</div>
+<div class="paragraph">
+<p>For example the Salesforce CRM connector is simply a set of actions that pull and push data into the CRM. It is then
+just a matter of setting up the proper rules with the proper conditions to determine when and how the data will be
+pulled or pushed into the third-party system.</p>
+</div>
+<div class="sect3">
+<h4 id="_actions">2.7.1. Actions</h4>
+<div class="paragraph">
+<p>Actions are executed by rules in a sequence, and an action is only executed once the previous action has finished
+executing. If an action generates an exception, it will be logged and the execution sequence will continue unless in the
+case of a Runtime exception (such as a NullPointerException).</p>
+</div>
+<div class="paragraph">
+<p>Actions are implemented as Java classes, and as such may perform any kind of tasks that may include calling web hooks,
+setting profile properties, extracting data from the incoming request (such as resolving location from an IP address),
+or even pulling and/or pushing data to third-party systems such as a CRM server.</p>
+</div>
+<div class="paragraph">
+<p>Apache Unomi also comes with built-in actions. You may find the list of built-in actions in the <a href="#_built_in_actions">Built-in actions</a> section.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_request_flow">2.8. Request flow</h3>
+<div class="paragraph">
+<p>Here is an overview of how Unomi processes incoming requests to the <code>ContextServlet</code>.</p>
+</div>
+<div class="imageblock">
+<div class="content">
+<img src="images/unomi-request.png" alt="Unomi request overview">
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_first_steps_with_apache_unomi">3. First steps with Apache Unomi</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_getting_started_with_unomi">3.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">3.1.1. Prerequisites</h4>
+<div class="paragraph">
+<p>This document assumes that you are already familiar with Unomi&#8217;s <a href="#_concepts">concepts</a>. On the technical side, we
+also assume 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 7 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 7 or greater.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_running_unomi">3.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="#_5-min-quickstart">5 minute quick start</a> or by compiling using the building <a href="building-and-deploying.html#Deploying_the_generated_package">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>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">3.2. Recipes</h3>
+<div class="sect3">
+<h4 id="_introduction">3.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">3.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.</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
+}
+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">3.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" : "john.doe@acme.com"
+            }
+        }
+    ]
+}
+EOF</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_search_for_profile_events">3.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">3.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">3.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">3.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">3.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">3.3. Request examples</h3>
+<div class="sect3">
+<h4 id="_retrieving_your_first_context">3.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">3.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">3.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.</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
+}
+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">3.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">3.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">3.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">3.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">3.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">3.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">3.4.3. Tracking page views</h4>
+<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">3.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="johndoe@acme.com"/&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" : "johndoe@acme.com",
+    "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">3.5. Configuration</h3>
+<div class="sect3">
+<h4 id="_centralized_configuration">3.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">3.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">3.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">3.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">3.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">3.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">3.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

[... 4162 lines stripped ...]