blob: 44d6e189f16dc6932ce78f2e42c26515af2383bb [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<title>Storm MongoDB Integration</title>
<!-- Bootstrap core CSS -->
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap theme -->
<link href="/assets/css/bootstrap-theme.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link rel="stylesheet" href="http://fortawesome.github.io/Font-Awesome/assets/font-awesome/css/font-awesome.css">
<link href="/css/style.css" rel="stylesheet">
<link href="/assets/css/owl.theme.css" rel="stylesheet">
<link href="/assets/css/owl.carousel.css" rel="stylesheet">
<script type="text/javascript" src="/assets/js/jquery.min.js"></script>
<script type="text/javascript" src="/assets/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/assets/js/owl.carousel.min.js"></script>
<script type="text/javascript" src="/assets/js/storm.js"></script>
<!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
<!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<header>
<div class="container-fluid">
<div class="row">
<div class="col-md-5">
<a href="/index.html"><img src="/images/logo.png" class="logo" /></a>
</div>
<div class="col-md-5">
<h1>Version: 1.0.6</h1>
</div>
<div class="col-md-2">
<a href="/downloads.html" class="btn-std btn-block btn-download">Download</a>
</div>
</div>
</div>
</header>
<!--Header End-->
<!--Navigation Begin-->
<div class="navbar" role="banner">
<div class="container-fluid">
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".bs-navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<nav class="collapse navbar-collapse bs-navbar-collapse" role="navigation">
<ul class="nav navbar-nav">
<li><a href="/index.html" id="home">Home</a></li>
<li><a href="/getting-help.html" id="getting-help">Getting Help</a></li>
<li><a href="/about/integrates.html" id="project-info">Project Information</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" id="documentation">Documentation <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/releases/2.0.0-SNAPSHOT/index.html">2.0.0-SNAPSHOT</a></li>
<li><a href="/releases/1.2.1/index.html">1.2.1</a></li>
<li><a href="/releases/1.1.2/index.html">1.1.2</a></li>
<li><a href="/releases/1.0.6/index.html">1.0.6</a></li>
</ul>
</li>
<li><a href="/talksAndVideos.html">Talks and Slideshows</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" id="contribute">Community <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/contribute/Contributing-to-Storm.html">Contributing</a></li>
<li><a href="/contribute/People.html">People</a></li>
<li><a href="/contribute/BYLAWS.html">ByLaws</a></li>
</ul>
</li>
<li><a href="/2018/02/19/storm121-released.html" id="news">News</a></li>
</ul>
</nav>
</div>
</div>
<div class="container-fluid">
<h1 class="page-title">Storm MongoDB Integration</h1>
<div class="row">
<div class="col-md-12">
<!-- Documentation -->
<p class="post-meta"></p>
<div class="documentation-content"><p>Storm/Trident integration for <a href="https://www.mongodb.org/">MongoDB</a>. This package includes the core bolts and trident states that allows a storm topology to either insert storm tuples in a database collection or to execute update queries against a database collection in a storm topology.</p>
<h2 id="insert-into-database">Insert into Database</h2>
<p>The bolt and trident state included in this package for inserting data into a database collection.</p>
<h3 id="mongomapper">MongoMapper</h3>
<p>The main API for inserting data in a collection using MongoDB is the <code>org.apache.storm.mongodb.common.mapper.MongoMapper</code> interface:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MongoMapper</span> <span class="kd">extends</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="n">Document</span> <span class="nf">toDocument</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<h3 id="simplemongomapper">SimpleMongoMapper</h3>
<p><code>storm-mongodb</code> includes a general purpose <code>MongoMapper</code> implementation called <code>SimpleMongoMapper</code> that can map Storm tuple to a Database document. <code>SimpleMongoMapper</code> assumes that the storm tuple has fields with same name as the document field name in the database collection that you intend to write to.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleMongoMapper</span> <span class="kd">implements</span> <span class="n">MongoMapper</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">String</span><span class="o">[]</span> <span class="n">fields</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Document</span> <span class="nf">toDocument</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Document</span> <span class="n">document</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Document</span><span class="o">();</span>
<span class="k">for</span><span class="o">(</span><span class="n">String</span> <span class="n">field</span> <span class="o">:</span> <span class="n">fields</span><span class="o">){</span>
<span class="n">document</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="n">tuple</span><span class="o">.</span><span class="na">getValueByField</span><span class="o">(</span><span class="n">field</span><span class="o">));</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">document</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">SimpleMongoMapper</span> <span class="nf">withFields</span><span class="o">(</span><span class="n">String</span><span class="o">...</span> <span class="n">fields</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">fields</span> <span class="o">=</span> <span class="n">fields</span><span class="o">;</span>
<span class="k">return</span> <span class="k">this</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h3 id="mongoinsertbolt">MongoInsertBolt</h3>
<p>To use the <code>MongoInsertBolt</code>, you construct an instance of it by specifying url, collectionName and a <code>MongoMapper</code> implementation that converts storm tuple to DB document. The following is the standard URI connection scheme:
<code>mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]</code></p>
<p>More options information(eg: Write Concern Options) about Mongo URI, you can visit <a href="https://docs.mongodb.org/manual/reference/connection-string/#connections-connection-options">https://docs.mongodb.org/manual/reference/connection-string/#connections-connection-options</a></p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">String</span> <span class="n">url</span> <span class="o">=</span> <span class="s">"mongodb://127.0.0.1:27017/test"</span><span class="o">;</span>
<span class="n">String</span> <span class="n">collectionName</span> <span class="o">=</span> <span class="s">"wordcount"</span><span class="o">;</span>
<span class="n">MongoMapper</span> <span class="n">mapper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleMongoMapper</span><span class="o">()</span>
<span class="o">.</span><span class="na">withFields</span><span class="o">(</span><span class="s">"word"</span><span class="o">,</span> <span class="s">"count"</span><span class="o">);</span>
<span class="n">MongoInsertBolt</span> <span class="n">insertBolt</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MongoInsertBolt</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">collectionName</span><span class="o">,</span> <span class="n">mapper</span><span class="o">);</span>
</code></pre></div>
<h3 id="mongotridentstate">MongoTridentState</h3>
<p>We also support a trident persistent state that can be used with trident topologies. To create a Mongo persistent trident state you need to initialize it with the url, collectionName, the <code>MongoMapper</code> instance. See the example below:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"> <span class="n">MongoMapper</span> <span class="n">mapper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleMongoMapper</span><span class="o">()</span>
<span class="o">.</span><span class="na">withFields</span><span class="o">(</span><span class="s">"word"</span><span class="o">,</span> <span class="s">"count"</span><span class="o">);</span>
<span class="n">MongoState</span><span class="o">.</span><span class="na">Options</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MongoState</span><span class="o">.</span><span class="na">Options</span><span class="o">()</span>
<span class="o">.</span><span class="na">withUrl</span><span class="o">(</span><span class="n">url</span><span class="o">)</span>
<span class="o">.</span><span class="na">withCollectionName</span><span class="o">(</span><span class="n">collectionName</span><span class="o">)</span>
<span class="o">.</span><span class="na">withMapper</span><span class="o">(</span><span class="n">mapper</span><span class="o">);</span>
<span class="n">StateFactory</span> <span class="n">factory</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MongoStateFactory</span><span class="o">(</span><span class="n">options</span><span class="o">);</span>
<span class="n">TridentTopology</span> <span class="n">topology</span> <span class="o">=</span> <span class="k">new</span> <span class="n">TridentTopology</span><span class="o">();</span>
<span class="n">Stream</span> <span class="n">stream</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">newStream</span><span class="o">(</span><span class="s">"spout1"</span><span class="o">,</span> <span class="n">spout</span><span class="o">);</span>
<span class="n">stream</span><span class="o">.</span><span class="na">partitionPersist</span><span class="o">(</span><span class="n">factory</span><span class="o">,</span> <span class="n">fields</span><span class="o">,</span> <span class="k">new</span> <span class="n">MongoStateUpdater</span><span class="o">(),</span> <span class="k">new</span> <span class="n">Fields</span><span class="o">());</span>
</code></pre></div>
<p><strong>NOTE</strong>:</p>
<blockquote>
<p>If there is no unique index provided, trident state inserts in the case of failures may result in duplicate documents.</p>
</blockquote>
<h2 id="update-from-database">Update from Database</h2>
<p>The bolt included in this package for updating data from a database collection.</p>
<h3 id="simplemongoupdatemapper">SimpleMongoUpdateMapper</h3>
<p><code>storm-mongodb</code> includes a general purpose <code>MongoMapper</code> implementation called <code>SimpleMongoUpdateMapper</code> that can map Storm tuple to a Database document. <code>SimpleMongoUpdateMapper</code> assumes that the storm tuple has fields with same name as the document field name in the database collection that you intend to write to.
<code>SimpleMongoUpdateMapper</code> uses <code>$set</code> operator for setting the value of a field in a document. More information about update operator, you can visit
<a href="https://docs.mongodb.org/manual/reference/operator/update/">https://docs.mongodb.org/manual/reference/operator/update/</a></p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleMongoUpdateMapper</span> <span class="kd">implements</span> <span class="n">MongoMapper</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">String</span><span class="o">[]</span> <span class="n">fields</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Document</span> <span class="nf">toDocument</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Document</span> <span class="n">document</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Document</span><span class="o">();</span>
<span class="k">for</span><span class="o">(</span><span class="n">String</span> <span class="n">field</span> <span class="o">:</span> <span class="n">fields</span><span class="o">){</span>
<span class="n">document</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="n">tuple</span><span class="o">.</span><span class="na">getValueByField</span><span class="o">(</span><span class="n">field</span><span class="o">));</span>
<span class="o">}</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Document</span><span class="o">(</span><span class="s">"$set"</span><span class="o">,</span> <span class="n">document</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">SimpleMongoUpdateMapper</span> <span class="nf">withFields</span><span class="o">(</span><span class="n">String</span><span class="o">...</span> <span class="n">fields</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">fields</span> <span class="o">=</span> <span class="n">fields</span><span class="o">;</span>
<span class="k">return</span> <span class="k">this</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h3 id="queryfiltercreator">QueryFilterCreator</h3>
<p>The main API for creating a MongoDB query Filter is the <code>org.apache.storm.mongodb.common.QueryFilterCreator</code> interface:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">QueryFilterCreator</span> <span class="kd">extends</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="n">Bson</span> <span class="nf">createFilter</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<h3 id="simplequeryfiltercreator">SimpleQueryFilterCreator</h3>
<p><code>storm-mongodb</code> includes a general purpose <code>QueryFilterCreator</code> implementation called <code>SimpleQueryFilterCreator</code> that can create a MongoDB query Filter by given Tuple. <code>QueryFilterCreator</code> uses <code>$eq</code> operator for matching values that are equal to a specified value. More information about query operator, you can visit
<a href="https://docs.mongodb.org/manual/reference/operator/query/">https://docs.mongodb.org/manual/reference/operator/query/</a></p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleQueryFilterCreator</span> <span class="kd">implements</span> <span class="n">QueryFilterCreator</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">field</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Bson</span> <span class="nf">createFilter</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Filters</span><span class="o">.</span><span class="na">eq</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="n">tuple</span><span class="o">.</span><span class="na">getValueByField</span><span class="o">(</span><span class="n">field</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">SimpleQueryFilterCreator</span> <span class="nf">withField</span><span class="o">(</span><span class="n">String</span> <span class="n">field</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">field</span> <span class="o">=</span> <span class="n">field</span><span class="o">;</span>
<span class="k">return</span> <span class="k">this</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h3 id="mongoupdatebolt">MongoUpdateBolt</h3>
<p>To use the <code>MongoUpdateBolt</code>, you construct an instance of it by specifying Mongo url, collectionName, a <code>QueryFilterCreator</code> implementation and a <code>MongoMapper</code> implementation that converts storm tuple to DB document.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"> <span class="n">MongoMapper</span> <span class="n">mapper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleMongoUpdateMapper</span><span class="o">()</span>
<span class="o">.</span><span class="na">withFields</span><span class="o">(</span><span class="s">"word"</span><span class="o">,</span> <span class="s">"count"</span><span class="o">);</span>
<span class="n">QueryFilterCreator</span> <span class="n">updateQueryCreator</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleQueryFilterCreator</span><span class="o">()</span>
<span class="o">.</span><span class="na">withField</span><span class="o">(</span><span class="s">"word"</span><span class="o">);</span>
<span class="n">MongoUpdateBolt</span> <span class="n">updateBolt</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MongoUpdateBolt</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">collectionName</span><span class="o">,</span> <span class="n">updateQueryCreator</span><span class="o">,</span> <span class="n">mapper</span><span class="o">);</span>
<span class="c1">//if a new document should be inserted if there are no matches to the query filter</span>
<span class="c1">//updateBolt.withUpsert(true);</span>
</code></pre></div>
<p>Or use a anonymous inner class implementation for <code>QueryFilterCreator</code>:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"> <span class="n">MongoMapper</span> <span class="n">mapper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleMongoUpdateMapper</span><span class="o">()</span>
<span class="o">.</span><span class="na">withFields</span><span class="o">(</span><span class="s">"word"</span><span class="o">,</span> <span class="s">"count"</span><span class="o">);</span>
<span class="n">QueryFilterCreator</span> <span class="n">updateQueryCreator</span> <span class="o">=</span> <span class="k">new</span> <span class="n">QueryFilterCreator</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Bson</span> <span class="nf">createFilter</span><span class="o">(</span><span class="n">ITuple</span> <span class="n">tuple</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Filters</span><span class="o">.</span><span class="na">gt</span><span class="o">(</span><span class="s">"count"</span><span class="o">,</span> <span class="mi">3</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="n">MongoUpdateBolt</span> <span class="n">updateBolt</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MongoUpdateBolt</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">collectionName</span><span class="o">,</span> <span class="n">updateQueryCreator</span><span class="o">,</span> <span class="n">mapper</span><span class="o">);</span>
<span class="c1">//if a new document should be inserted if there are no matches to the query filter</span>
<span class="c1">//updateBolt.withUpsert(true);</span>
</code></pre></div>
<h2 id="license">License</h2>
<p>Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
&quot;License&quot;); you may not use this file except in compliance
with the License. You may obtain a copy of the License at</p>
<p><a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a></p>
<p>Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
&quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.</p>
<h2 id="committer-sponsors">Committer Sponsors</h2>
<ul>
<li>Sriharsha Chintalapani (<a href="mailto:sriharsha@apache.org">sriharsha@apache.org</a>)</li>
</ul>
</div>
</div>
</div>
</div>
<footer>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="footer-widget">
<h5>Meetups</h5>
<ul class="latest-news">
<li><a href="http://www.meetup.com/Apache-Storm-Apache-Kafka/">Apache Storm & Apache Kafka</a> <span class="small">(Sunnyvale, CA)</span></li>
<li><a href="http://www.meetup.com/Apache-Storm-Kafka-Users/">Apache Storm & Kafka Users</a> <span class="small">(Seattle, WA)</span></li>
<li><a href="http://www.meetup.com/New-York-City-Storm-User-Group/">NYC Storm User Group</a> <span class="small">(New York, NY)</span></li>
<li><a href="http://www.meetup.com/Bay-Area-Stream-Processing">Bay Area Stream Processing</a> <span class="small">(Emeryville, CA)</span></li>
<li><a href="http://www.meetup.com/Boston-Storm-Users/">Boston Realtime Data</a> <span class="small">(Boston, MA)</span></li>
<li><a href="http://www.meetup.com/storm-london">London Storm User Group</a> <span class="small">(London, UK)</span></li>
<!-- <li><a href="http://www.meetup.com/Apache-Storm-Kafka-Users/">Seatle, WA</a> <span class="small">(27 Jun 2015)</span></li> -->
</ul>
</div>
</div>
<div class="col-md-3">
<div class="footer-widget">
<h5>About Storm</h5>
<p>Storm integrates with any queueing system and any database system. Storm's spout abstraction makes it easy to integrate a new queuing system. Likewise, integrating Storm with database systems is easy.</p>
</div>
</div>
<div class="col-md-3">
<div class="footer-widget">
<h5>First Look</h5>
<ul class="footer-list">
<li><a href="/releases/current/Rationale.html">Rationale</a></li>
<li><a href="/releases/current/Tutorial.html">Tutorial</a></li>
<li><a href="/releases/current/Setting-up-development-environment.html">Setting up development environment</a></li>
<li><a href="/releases/current/Creating-a-new-Storm-project.html">Creating a new Storm project</a></li>
</ul>
</div>
</div>
<div class="col-md-3">
<div class="footer-widget">
<h5>Documentation</h5>
<ul class="footer-list">
<li><a href="/releases/current/index.html">Index</a></li>
<li><a href="/releases/current/javadocs/index.html">Javadoc</a></li>
<li><a href="/releases/current/FAQ.html">FAQ</a></li>
</ul>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
<p align="center">Copyright © 2015 <a href="http://www.apache.org">Apache Software Foundation</a>. All Rights Reserved.
<br>Apache Storm, Apache, the Apache feather logo, and the Apache Storm project logos are trademarks of The Apache Software Foundation.
<br>All other marks mentioned may be trademarks or registered trademarks of their respective owners.</p>
</div>
</div>
</div>
</footer>
<!--Footer End-->
<!-- Scroll to top -->
<span class="totop"><a href="#"><i class="fa fa-angle-up"></i></a></span>
</body>
</html>