Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
332 lines (288 sloc) 13.3 KB
---
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
title: "JDisc Container Components"
---
<p>
This document explains the common concepts necessary to develop all types of JDisc Container components.
A basic knowledge of the JDisc Container is required.
</p><p>
All components must extend a base class from the JDisc Container code module.
For example, searchers must extend the class <code>com.yahoo.search.Searcher</code>.
The main available component types are:
<ul>
<li><a href="processing.html">processors</a></li>
<li><a href="../searcher-development.html">searchers</a></li>
<li><a href="../docproc-development.html">document processors</a></li>
<li><a href="../result-rendering.html">search result renderers</a></li>
<li><a href="injecting-components.html#implement-provider">provider components</a>.</li>
</ul>
Searchers and document processors belong to a subclass of components
called <a href="../chained-components.html">chained components</a>.
For an introduction to how the different component types interact,
refer to the <a href="../component-types.html">overview of component types</a>.
</p><p>
The components of the search container are usually deployed as part of
an <a href="developing-osgi-bundles.html">OSGi bundle</a>.
Build the bundles using maven and the <a href="../bundle-plugin.html">bundle plugin</a>.
</p>
<h2 id="concurrency">Concurrency</h2>
<p>
Components will be executed concurrently by multiple threads.
This places a very important constraint on all component classes:
<em>non-final instance variables are not safe.</em>
They must be eliminated, or made thread-safe somehow.
</p>
<h2 id="resource-management">Resource management</h2>
<p>
Components that use threads, file handles or other native resources
that needs to be released when the component falls out of scope,
must override a method called <code>deconstruct</code>.
Here is an example implementation from a component that uses a thread pool named 'executor':
<pre>
@Override
public void deconstruct() {
super.deconstruct();
try {
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
</pre>
Note that it is always advisable to call the super-method first.
</p>
<h2 id="dependency-injection">Dependency injection</h2>
<p>
The components might need to access resources, such as other components or config.
These are injected directly into the constructor.
The following types of constructor dependencies are allowed:
<ul>
<li><a href="../configuring-components.html">Config objects</a></li>
<li><a href="injecting-components.html">Other components</a></li>
<li><a href="../reference/statistics-api.html">The Statistics library</a></li>
<li>The Linguistics library</li> <!-- ToDo link here -->
</ul>
Ideally, any class should have only one public constructor,
but if the component needs more than one, annotate the one to be
used by the container with <code>@Inject</code> (full name:
<code>com.google.inject.Inject)</code>.
</p>
<h2 id="deploying-a-component">Deploying a Component</h2>
<p>
The container will create one or more instances of the component,
as specified in <a href="#adding-component-to-application-package">the application package</a>.
The container will create a new instance of this component only when it is reconfigured,
so any data needed by the component can be read and prepared from a constructor in the component.
</p><p>
See the full API available to components at the
<a href="http://javadoc.io/page/com.yahoo.vespa/container-core/latest/com/yahoo/container/package-summary.html">
Container Javadoc</a>.
</p><p>
Once the component passes unit tests, it can be deployed.
The steps involved are building the component jar file, adding it to
the Vespa application package and deploying the application package.
These steps are described in the following sections, using a searcher as example.
</p>
<h3 id="building-the-plugin-jar">Building the Plugin .jar</h3>
<p>
To build the plugin jar, call <code>mvn install</code> in the project directory.
It can then be found in the target directory, and will have the suffix <em>-deploy.jar</em>.
</p><p>
Assume for the rest of the document that the artifactId
is <code>com.yahoo.search.example.SimpleSearcher</code> and the version is <code>1.0</code>.
The plugin built will then have the name <em>com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar</em>.
</p>
<h3 id="adding-component-to-application-package">Adding the Plugin to the Vespa Application Package</h3>
<p>
The previous step should produce a plugin jar file, which may now be
deployed to Vespa by adding it to
an <a href="../cloudconfig/application-packages.html">application package</a>:
A directory containing at minimum <em>hosts.xml</em> and <em>services.xml</em>.
<ul>
<li>placing
the <code>com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar</code>
the <code>components/</code> directory under the application
package root</li>
<li>Changing <code>services.xml</code> to include the Searcher</li>
</ul>
To include the searcher in <em>services.xml</em>,
define a search chain and add the searcher to it. Example:
<pre>
&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;services version="1.0"&gt;
&lt;admin version="2.0"&gt;
&lt;adminserver hostalias="node1" /&gt;
&lt;/configservers&gt;
&lt;logserver hostalias="node1" /&gt;
&lt;/admin&gt;
&lt;container version="1.0"&gt;
&lt;search&gt;
&lt;chain id="default" inherits="vespa"&gt;
&lt;searcher id="com.yahoo.search.example.SimpleSearcher"/&gt;
&lt;/chain&gt;
&lt;/search&gt;
&lt;nodes&gt;
&lt;node hostalias="node1" /&gt;
&lt;/nodes&gt;
&lt;/container&gt;
&lt;/services&gt;
</pre>
The searcher id above is resolved to the plugin jar we added by
the <code>Bundle-SymbolicName</code>
(<a href="developing-osgi-bundles.html">a field in the manifest of the jar file</a>),
which is determined by the <code>artifactId</code>,
and to the right class within the bundle by the class name.
By keeping the <code>searcher id</code>, <code>class name</code>
and <code>artifactId</code> the same, we keep things simple,
but more advanced use where this is possible is also supported.
This will be explained in later sections.
</p><p>
For a reference to these tags,
see <a href="../reference/services-search.html#chain">the search chains reference</a>.
</p><p>
Example <code>hosts.xml</code>:
<pre>
&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;hosts&gt;
&lt;host name="localhost"&gt;
&lt;alias&gt;node1&lt;/alias&gt;
&lt;/host&gt;
&lt;/hosts&gt;
</pre>
By creating a directory containing this <code>services.xml</code>,
<code>hosts.xml</code>
and <code>components/com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar</code>, that directory becomes
a complete application package containing a bundle, which can now be deployed.
</p>
<h3 id="deploying-the-application-package">Deploying the Application Package</h3>
<p>
Set up a Vespa instance using the <a href="../vespa-quick-start.html">quick start</a>.
Once the component and the config is added to the application package,
it can be <a href="../cloudconfig/application-packages.html#deploy">deployed</a>
by running <code>vespa-deploy</code>.
These steps will copy any changed bundles to the nodes in the cluster which needs them
and switch queries over to running the new component versions.
</p><p>
This works safely without requiring any processes to be restarted,
even if the application package contains changes to classes which are already running queries.
The switch is atomic from the point of view of the query -
all queries will execute to completion,
either using only the components of the last version of the application package
or only the new ones, so interdependent changes in multiple searcher components
can be deployed without problems.
</p>
<h4 id="JNI-requires-restart">JNI requires restart</h4>
<p>
The exception to the above is bundles containing JNI packages.
There can only be one instance of the native library - a bundle hence cannot reload.
Best practice is to load the JNI library in the constructor,
as this will cause the new bundle <em>not</em> to load, but continue on the current version.
A subsequent restart will load the new bundle.
This will hence not cause failures.
Alternatively, if the JNI library is initialized lazily (e.g. on first invocation),
bundle reloads will succeed, but subsequent invocations of code using the JNI library will fail.
Hence, the new version will run, but fail.
</p><p>
A warning is issued in the log when deploying rather than the normal
<em>Switching to the latest deployed set of handlers</em> - example:
<pre>
[2016-09-21 14:22:05.387] WARNING : qrserver stderr Cannot load mylib native library
</pre>
To minimize restarts, it is recommended to put JNI components in minimal, separate bundles.
This will prevent reload of the JNI-bundles, unless the JNI-bundle itself is changed.
</p>
<h4 id="troubleshooting">Troubleshooting</h4>
<p>
If there is some error in the application package, it will usually be
detected during the <code>vespa-deploy prepare</code> step and cause an error message.
However, some classes of errors are only detected once the application is deployed.
When redeploying an application, it is therefore recommended to watch the vespa log by running:
<pre>
vespa-logfmt -N
</pre>
The new application is active after the INFO message:
<pre>
Switched to the latest deployed set of handlers...;
</pre>
If this message does not appear after a reasonable amount of time
after completion of <code>vespa-deploy activate</code>,
one will see some errors or warnings instead, that will help debug the application.
</p>
<h4 id="monitoring-the-active-application">Monitoring the active Application</h4>
<p>
All containers also provide a built-in handler that outputs JSON
formatted information about the active application, including its
components and chains (it can also be configured to show <a
href="../reference/application-packages-reference.html#versioning-application-packages">a
user-defined version</a>). The handler answers to requests with the path
<code>/ApplicationStatus</code>. For example, if 'localhost' runs a
a container with HTTP configured on port 8080:
<pre>
http://localhost:8080/ApplicationStatus
</pre>
In order to view nicely formatted output in a web browser, it might be
necessary to install a browser extension, like <code>JSONView</code> for Firefox.
</p>
<h3 id="including-third-party-libraries">Including third-party libraries</h3>
<p>
External dependencies <a href="../bundle-plugin.html">can be included into the bundle.</a>
</p>
<h3 id="exporting-importing-and-including-packages-from-bundles">Exporting, importing and including packages in bundles</h3>
<p>
<a href="../bundle-plugin.html">OSGi features information hiding -
by default all the classes used inside a bundle are invisible from the outside.</a>
</p>
<h3 id="packagelist">Global and exported packages</h3>
<p>
The JDisc Container has one set of <em>global</em> packages.
These are packages that are available with no import,
and constitutes the supported API of the JDisc Container.
Backwards incompatible changes are not made to these packages.
</p><p>
There is also a set of <em>exported</em> packages.
These are available for import, and includes all legacy packages,
plus extension packages which are not part of the core API.
Note that these are not considered to be "public" APIs, as global packages are,
and backwards incompatible changes <em>can</em> be made to these packages,
or they may be removed.
</p><p>
The list of exported and global packages is available in the
<a href="https://github.com/vespa-engine/vespa/blob/master/container-core/pom.xml">container-core
pom.xml</a>, in <code>project/properties/exportedPackages</code>
and <code>project/properties/globalPackages</code>.
</p>
<h3 id="versions">Versions</h3>
<p>
All the elements of the search container which may be referenced by an
id may be <em>versioned</em>, that includes chains, components and query profiles.
This allows multiple versions of these elements to be used at the same time,
including multiple versions of the same classes,
which is handy for bucket testing new versions.
</p><p>
An id or id reference may include a version by using the following syntax: <code>name:version</code>.
This works with ids in search requests, services.xml, code and query profiles.
</p><p>
A version has the format:
<pre>
major.minor.micro.qualifier
</pre>
where major, minor and micro are integers and qualifier is a string.
Any right-hand portion of the version string may be skipped.
In <em>versions</em>, skipped values mean "0" (and <em>empty</em> for the qualifier).
In <em>version references</em> skipped values means "unspecified".
Any unspecified number will be matched to the highest number available,
while a qualifier specified <em>must</em> be matched exactly if it is specified
(qualifiers are rarely used).
</p><p>
To specify the version of a bundle, specify version in pom.xml
(we recommend not using <em>qualifier</em>):
<pre>
&lt;groupId&gt;com.yahoo.example&lt;/groupId&gt;
&lt;artifactId&gt;MyPlugin&lt;/artifactId&gt;
&lt;version&gt;<strong>major.minor.micro</strong>&lt;/version&gt;
</pre>
This will automatically be used to set the <code>Bundle-Version</code> in the bundle manifest.
</p><p>
For more details, see <a href="component-versioning.html">component versioning</a>.
</p>