OSGi has become a platform capable of running large applications for a variety of purposes, including rich client applications, server-side systems and cloud and container based architectures. As these applications are generally based on many bundles, describing each bundle individually in an application definition becomes unwieldy once the number of bundles reaches a certain level.
When developing large scale applications it is often the case that few people know the role of every single bundle or configuration item in the application. To keep the architecture understandable a grouping mechanism is needed that allows for the representation of parts of the application into larger entities that keep reasoning about the system manageable. In such a domain members of teams spread across an organization will need to be able to both develop new parts for the application as well as make tweaks or enhancements to parts developed by others such as adding configuration and resources or changing one or more bundles relevant to their part of the application.
The higher level constructs that define the application should be reusable in different contexts, for example if one team has developed a component to handle job processing, different applications should be able to use it, and if needed tune its configuration or other aspects so that it works in each setting without having to know each and every detail that the job processing component is built up from.
Applications are often associated with additional resources or metadata, for example database scripts or custom artifacts. By including these with the application definition, all the related entities are encapsulated in a single artifact.
By combining various applications or subsystems together, systems are composed of existing, reusable building blocks, where all these blocks can work together. Architects of these systems need to think about components without having to dive into the individual implementation details of each subcomponent. The Features defined in this specification can be used to model such applications. Features contain the definition of an application or component and may be composed into larger systems.
-
Declarative - Features are declarative and can be mapped to different implementations.
-
Extensible - Features are extensible with custom content to facilitate all information related to a Feature to be co-located.
-
Human Readable - No special software is needed to read or author Features.
-
Machine Readable - Features are easily be processed by tools.
The following entities are used in this specification:
-
Feature - A Feature contains a number of entities that, when provided to a launcher can be turned into an executable system. Features are building blocks which may be assembled into larger systems.
-
Bundles - A Feature can contain one ore more bundles.
-
Configuration - A Feature can contain configurations for the Configuration Admin service.
-
Extension - A Feature can contain a number of extensions with custom content.
-
Launcher - A launcher turns one or more Features into an executable system.
-
Processor - A Feature processor reads Features and perform a processing operation on them, such as validation, transformation or generation of new entities based on the Features.
-
Properties - Framework launching properties can be specified in a Feature.
Features are defined by declaring JSON documents or by using the Feature API. Each Feature has a unique ID which includes a version. It holds a number of entities, including a list of bundles, configurations and others. Features are extensible, that is a Feature can also contain any number of custom entities which are related to the Feature.
Features may have dependencies on other Features. Features inherit the capabilities and requirements from all bundles listed in the Feature.
Once created, a Feature is immutable. Its definition cannot be modified. However it is possible to record caching related information in a Feature through transient extensions. This cached content is not significant for the definition of the Feature or part of its identity.
Identifiers used throughout this specification are defined using the Maven Identifier model. They are composed of the following parts:
-
Group ID
-
Artifact ID
-
Version
-
Type (optional)
-
Classifier (optional)
Note that if Version has the -SNAPSHOT
suffix, the identifier points at an unreleased artifact that is under
development and may still change.
For more information see [3] Apache Maven Pom Reference. The format used to specify identifiers is as follows:
groupId ':' artifactId ( ':' type ( ':' classifier )? )? ':' version
Each Feature has a unique identifier. Apart from providing a persistent handle to the Feature, it also provides enough information to find the Feature in an artifact repository. This identifier is defined using the format described in Identifiers.
A Feature can have the following attributes:
Table 159.1 Feature Attributes
Attribute | Data Type | Kind | Description |
---|---|---|---|
name | String | Optional | The short descriptive name of the Feature. |
categories | Array of String | Optional, defaults to an empty array | The categories this Feature belongs to. The values are user-defined. |
complete | boolean | Optional, defaults to false |
Completeness of the Feature. A Feature is complete when it has no external dependencies. |
description | String | Optional | A longer description of the Feature. |
docURL | String | Optional | A location where documentation can be found for the Feature. |
license | String | Optional | The license of the Feature. The license only relates to
the Feature itself and not to any artifacts that might be
referenced by the Feature. The license follows the
Bundle-License format as specified in the Core
specification.
|
SCM | String | Optional | SCM information relating to the feature. The syntax of the value follows the Bundle-SCM format. See the 'Bundle Manifest Headers' section in the OSGi Core specification. |
vendor | String | Optional | The vendor of the Feature. |
An initial Feature without content can be declared as follows:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:1.0.0",
"name": "The ACME app",
"description":
"This is the main ACME app, from where all functionality is reached."
/*
Additional Feature entities here
...
*/
}
Features can also be created, read and written using the Feature API. The main entry point for this API is the FeatureService. The Feature API uses the builder pattern to create entities used in Features.
A builder instance is used to create a single entity and cannot be re-used to create a second one. Builders are created from the BuilderFactory, which is available from the FeatureService through getBuilderFactory().
FeatureService fs = ... // from Service Registry
BuilderFactory factory = fs.getBuilderFactory();
FeatureBuilder builder = factory.newFeatureBuilder(
fs.getID("org.acme", "acmeapp", "1.0.0"));
builder.setName("The ACME app");
builder.setDescription("This is the main ACME app, "
+ "from where all functionality is reached.");
Feature f = builder.build();
The Feature API can also be useful in environments outside of an
OSGi Framework where no service registry is available, for example in a
build-system environment. In such environments the FeatureService can be
obtained by using the java.util.ServiceLoader
mechanism.
Comments in the form of [2] JSMin (The JavaScript Minifier)
comments are supported, that is, any text on the same line after
//
is ignored and any text between /* */
is
ignored.
Features list zero or more bundles that implement the functionality
provided by the Feature. Bundles are listed by referencing them in the
bundles
array so that they can be resolved from a repository.
Bundles can have metadata associated with them, such as the relative start
order of the bundle in the Feature. Custom metadata may also be provided.
A single Feature can provide multiple versions of the same bundle, if
desired.
Bundles are referenced using the identifier format described in
Identifiers. This means that Bundles are
referenced using their Maven coordinates. The bundles
array
contains JSON objects which can contain the bundle IDs and specify
optional additional metadata.
Arbitrary key-value pairs can be associated with bundle entries to store custom metadata alongside the bundle references. Reverse DNS naming should be used with the keys to avoid name clashes when metadata is provided by multiple entities. Keys not using the reverse DNS naming scheme are reserved for OSGi use.
Bundle metadata supports string
keys and
string
, number
or boolean
values.
The following example shows a simple Feature describing a small application with its dependencies:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:1.0.1",
"name": "The Acme Application",
"license": "https://opensource.org/licenses/Apache-2.0",
"complete": true,
"bundles": [
{ "id": "org.osgi:org.osgi.util.function:1.1.0" },
{ "id": "org.osgi:org.osgi.util.promise:1.1.1" },
{
"id": "org.apache.commons:commons-email:1.5",
// This attribute is used by custom tooling to
// find the associated javadoc
"org.acme.javadoc.link":
"https://commons.apache.org/proper/commons-email/javadocs/api-1.5"
},
{ "id": "com.acme:acmelib:1.7.2" }
]
/*
Additional Feature entities here
...
*/
}
A Feature with Bundles can be created using the Feature API as follows:
FeatureService fs = ... // from Service Registry
BuilderFactory factory = fs.getBuilderFactory();
FeatureBuilder builder = factory.newFeatureBuilder(
fs.getID("org.acme", "acmeapp", "1.0.1"));
builder.setName("The Acme Application");
builder.setLicense("https://opensource.org/licenses/Apache-2.0");
builder.setComplete(true);
FeatureBundle b1 = factory
.newBundleBuilder(fs.getIDfromMavenCoordinates(
"org.osgi:org.osgi.util.function:1.1.0"))
.build();
FeatureBundle b2 = factory
.newBundleBuilder(fs.getIDfromMavenCoordinates(
"org.osgi:org.osgi.util.promise:1.1.1"))
.build();
FeatureBundle b3 = factory
.newBundleBuilder(fs.getIDfromMavenCoordinates(
"org.apache.commons:commons-email:1.1.5"))
.addMetadata("org.acme.javadoc.link",
"https://commons.apache.org/proper/commons-email/javadocs/api-1.5")
.build();
FeatureBundle b4 = factory
.newBundleBuilder(fs.getIDfromMavenCoordinates(
"com.acme:acmelib:1.7.2"))
.build();
builder.addBundles(b1, b2, b3, b4);
Feature f = builder.build();
Features support configuration using the OSGi Configurator syntax,
see Configurator Specification. This is specified with the
configurations
key in the Feature. A Launcher can apply these
configurations to the Configuration Admin service when starting the
system.
It is an error to define the same PID twice in a single Feature. An entity processing the feature must fail in this case.
Example:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:osgifeature:configs:1.0.0",
"configurations": {
"org.apache.felix.http": {
"org.osgi.service.http.port": 8080,
"org.osgi.service.http.port.secure": 8443
}
}
}
Configurations and Framework Launching Properties support late binding of values. This enables setting these items through a Launcher, for example to specify a database user name, server port number or other information that may be variable between runtimes.
Variables are declared in the variables
section of the
Feature and they can have a default value specified. The default must be
of type string
, number
or boolean
.
Variables can also be declared to not have a default,
which means that they must be provided with a value through the Launcher.
This is done by specifying null
as the default in the
variable declaration.
Example:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:osgifeature:configs:1.1.0",
"variables": {
"http.port": 8080,
"db.username": "scott",
"db.password": null
},
"configurations": {
"org.acme.server.http": {
"org.osgi.service.http.port:Integer": "${http.port}"
},
"org.acme.db": {
"username": "${db.username}-user",
"password": "${db.password}"
}
}
}
Variables are referenced with the curly brace placeholder syntax:
${
variable-name
}
in the configuration value or framework launching property
value section. To support conversion of variables to non-string types the
configurator syntax specifying the datatype with the configuration key is
used, as in the above example.
Multiple variables can be referenced for a single configuration or
framework launching property value and variables may be combined with
text. If no variable exist with the given name, then the ${
variable-name
}
must be
retained in the value.
Features can include custom content. This makes it possible to keep custom entities and information relating to the Feature together with the rest of the Feature.
Custom content is provided through Feature extensions, which are in one of the following formats:
-
Text - A text extension contains an array of text.
-
JSON - A JSON extension contains embedded custom JSON content.
-
Artifacts - A list of custom artifacts associated with the Feature.
Extensions can have a variety of consumers. For example they may be handled by a Feature Launcher or by an external tool which can process the extension at any point of the Feature life cycle.
Extensions are of one of the following three kinds:
-
Mandatory - The entity processing this Feature must know how to handle this extension. If it cannot handle the extension it must fail.
-
Optional - This extension is optional. If the entity processing the Feature cannot handle it, the extension can be skipped or ignored. This is the default.
-
Transient - This extension contains transient information which may be used to optimize the processing of the Feature. It is not part of the Feature definition.
Extensions are specified as JSON objects under the
extensions
key in the Feature. A Feature can contain any
number of extensions, as long as the extension keys are unique. Extension
keys should use reverse domain naming to avoid name clashing of multiple
extensions in a single Feature. Extensions names without a reverse domain
naming prefix are reserved for OSGi use.
Text extensions support the addition of custom text content to the Feature. The text is provided as a JSON array of strings.
Example:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:2.0.0",
"name": "The Acme Application",
"license": "https://opensource.org/licenses/Apache-2.0",
"extensions": {
"org.acme.mydoc": {
"type": "text",
"text": [
"This application provides the main acme ",
"functionality."
]
}
}
}
Custom JSON content is added to Features by using a JSON extension. The content can either be a JSON object or a JSON array.
The following example extension declares under which execution environment the Feature is complete, using a custom JSON object.
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:2.1.0",
"name": "The Acme Application",
"license": "https://opensource.org/licenses/Apache-2.0",
"extensions": {
"org.acme.execution-environment": {
"type": "json",
"json": {
"environment-capabilities":
["osgi.ee; filter:=\"(&(osgi.ee=JavaSE)(version=11))\""],
"framework": "org.osgi:core:6.0.0",
"provided-features": ["org.acme:platform:1.1"]
}
}
}
}
Custom extensions can be used to associate artifacts that are not
listed as bundles
with the Feature.
For example, database definition resources may be listed as
artifacts in a Feature. In the following example, the extension
org.acme.ddlfiles
lists Database Definition Resources which
must be handled by the launcher agent, that is, the
database must be configured when the application is run:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:2.2.0",
"name": "The Acme Application",
"license": "https://opensource.org/licenses/Apache-2.0",
"complete": true,
"bundles": [
"org.osgi:org.osgi.util.function:1.1.0",
"org.osgi:org.osgi.util.promise:1.1.1",
"com.acme:acmelib:2.0.0"
],
"extensions": {
"org.acme.ddlfiles": {
"kind": "mandatory",
"type": "artifacts",
"artifacts": [
{ "id": "org.acme:appddl:1.2.1" },
{
"id": "org.acme:appddl-custom:1.0.3",
"org.acme.target": "custom-db"
}
]
}
}
}
As with bundle identifiers, custom artifacts are specified in an
object in the artifacts list with an explicit id
and
optional additional metadata. The keys of the metadata should use a
reverse domain naming pattern to avoid clashes. Keys that do not use
reverse domain name as a prefix are reserved for OSGi use. Supported
metadata values must be of type string
, number
or boolean
.
When a Feature is launched in an OSGi framework it may be necessary to specify Framework Properties. These are provided in the Framework Launching Properties extension section of the Feature. The Launcher must be able to satisfy the specified properties. If it cannot ensure that these are present in the running Framework the launcher must fail.
Framework Launching Properties can reference Variables as defined in Variables. These variables are substituted before the properties are set.
Example:
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:osgifeature:fw-props:2.0.0",
"variables": {
"fw.storage.dir": "/tmp" // Can be overridden through the launcher
},
"extensions": {
"framework-launching-properties": {
"type": "json",
"json": {
"org.osgi.framework.system.packages.extra":
"javax.activation;version=\"1.1.1\"",
"org.osgi.framework.bootdelegation": "javax.activation",
"org.osgi.framework.storage": "${fw.storage.dir}"
}
}
}
}
Feature JSON resources are versioned to support updates to the JSON
structure in the future. To declare the document version of the Feature
use the feature-resource-version
key in the JSON
document.
{
"feature-resource-version": "1.0",
"id": "org.acme:acmeapp:1.0.0"
/*
Additional Feature entities here
...
*/
}
The currently supported version of the Feature JSON documents is
1.0. If no Feature Resource Version is specified 1.0
is used
as the default.
The bundle providing the Feature Service must provide a capability
in the osgi.service
namespace representing the services it is registering. This capability
must also declare uses constraints for the relevant service
packages:
Provide-Capability: osgi.service;
objectClass:List<String>="org.osgi.service.feature.FeatureService";
uses:="org.osgi.service.feature"
This capability must follow the rules defined for the osgi.service Namespace.
Feature Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.feature; version="[1.0,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.feature; version="[1.0,1.1)"
-
BuilderFactory
- The Builder Factory can be used to obtain builders for the various entities. -
Feature
- The Feature Model Feature. -
FeatureArtifact
- An Artifact is an entity with an ID, for use in extensions. -
FeatureArtifactBuilder
- A builder for FeatureArtifact objects. -
FeatureBuilder
- A builder for Feature Models. -
FeatureBundle
- A Bundle which is part of a feature. -
FeatureBundleBuilder
- A builder for Feature Model FeatureBundle objects. -
FeatureConfiguration
- Represents an OSGi Configuration in the Feature Model. -
FeatureConfigurationBuilder
- A builder for Feature Model FeatureConfiguration objects. -
FeatureConstants
- Defines standard constants for the Feature specification. -
FeatureExtension
- A Feature Model Extension. -
FeatureExtension.Kind
- The kind of extension: optional, mandatory or transient. -
FeatureExtension.Type
- The type of extension -
FeatureExtensionBuilder
- A builder for Feature Model FeatureExtension objects. -
FeatureService
- The Feature service is the primary entry point for interacting with the feature model. -
ID
- ID used to denote an artifact.
The Builder Factory can be used to obtain builders for the various entities.
Consumers of this API must not implement this type
The artifact ID for the artifact object being built.
Obtain a new builder for Artifact objects.
The builder.
The ID for the bundle object being built. If the ID has no
type
specified, a default type of @{code jar} is
assumed.
Obtain a new builder for Bundle objects.
The builder.
The persistent ID for the Configuration being built.
Obtain a new builder for Configuration objects.
The builder.
The factory persistent ID for the Configuration being built.
The name of the configuration being built. The PID for the configuration will be the factoryPid + '~' + name
Obtain a new builder for Factory Configuration objects.
The builder.
The extension name.
The type of extension: JSON, Text or Artifacts.
The kind of extension: Mandatory, Optional or Transient.
Obtain a new builder for Feature objects.
The builder.
The Feature Model Feature.
Thread-safe
Consumers of this API must not implement this type
Get the bundles.
The bundles. The returned list is unmodifiable.
Get the categories.
The categories. The returned list is unmodifiable.
Get the configurations. The iteration order of the returned map should follow the definition order of the configurations in the feature.
The configurations. The returned map is unmodifiable.
Get the extensions. The iteration order of the returned map should follow the definition order of the extensions in the feature.
The extensions. The returned map is unmodifiable.
Get the license of this Feature. The syntax of the value follows the Bundle-License header syntax. See the 'Bundle Manifest Headers' section in the OSGi Core specification.
The license.
Get the SCM information relating to the feature. The syntax of the value follows the Bundle-SCM format. See the 'Bundle Manifest Headers' section in the OSGi Core specification.
The SCM information.
Get the variables. The iteration order of the returned map should follow
the definition order of the variables in the feature. Values are of type:
String, Boolean or BigDecimal for numbers. The null
JSON value is
represented by a null value in the map.
The variables. The returned map is unmodifiable.
An Artifact is an entity with an ID, for use in extensions.
Thread-safe
Consumers of this API must not implement this type
A builder for FeatureArtifact objects.
Not Thread-safe
Consumers of this API must not implement this type
Metadata key.
Metadata value.
Add metadata for this Artifact.
This builder.
The map with metadata.
Add metadata for this Artifact by providing a map. All metadata in the map is added to any previously provided metadata.
This builder.
A builder for Feature Models.
Not Thread-safe
Consumers of this API must not implement this type
The Bundles to add.
Add Bundles to the Feature.
This builder.
The Categories.
Adds one or more categories to the Feature.
This builder.
The Configurations to add.
Add Configurations to the Feature.
This builder.
The Extensions to add.
Add Extensions to the Feature
This builder.
The key.
The default value.
Add a variable to the Feature. If a variable with the specified key already exists it is replaced with this one. Variable values are of type: String, Boolean or BigDecimal for numbers.
This builder.
IllegalArgumentException
– if the value is of an invalid type.
to be added.
Add a map of variables to the Feature. Pre-existing variables with the same key in are overwritten if these keys exist in the map. Variable values are of type: String, Boolean or BigDecimal for numbers.
This builder.
IllegalArgumentException
– if a value is of an invalid type.
Build the Feature. Can only be called once on a builder. After calling this method the current builder instance cannot be used any more.
The Feature.
If the feature is complete.
Set the Feature Complete flag. If this method is not called the complete
flag defaults to false
.
This builder.
The description.
Set the Feature Description.
This builder.
The Documentation URL.
Set the documentation URL.
This builder.
The License.
Set the License.
This builder.
The Name.
Set the Feature Name.
This builder.
The SCM information.
Set the SCM information.
This builder.
A Bundle which is part of a feature.
Thread-safe
Consumers of this API must not implement this type
A builder for Feature Model FeatureBundle objects.
Not Thread-safe
Consumers of this API must not implement this type
Metadata key.
Metadata value.
Add metadata for this Bundle.
This builder.
The map with metadata.
Add metadata for this Bundle by providing a map. All metadata in the map is added to any previously provided metadata.
This builder.
Represents an OSGi Configuration in the Feature Model.
Thread-safe
Consumers of this API must not implement this type
Get the Factory PID from the configuration, if any.
The Factory PID, or null
if there is none.
A builder for Feature Model FeatureConfiguration objects.
Not Thread-safe
Consumers of this API must not implement this type
The configuration key.
The configuration value. Acceptable data types are the data type supported by the Configuration Admin service, which are the Primary Property Types as defined for the Filter Syntax in the OSGi Core specification.
Add a configuration value for this Configuration object. If a value with the same key was previously provided (regardless of case) the previous value is overwritten.
This builder.
IllegalArgumentException
– if the value is of an invalid type.
The map of configuration values to add. Acceptable value types are the data type supported by the Configuration Admin service, which are the Primary Property Types as defined for the Filter Syntax in the OSGi Core specification.
Add a map of configuration values for this Configuration object. Values will be added to any previously provided configuration values. If a value with the same key was previously provided (regardless of case) the previous value is overwritten.
This builder.
IllegalArgumentException
– if a value is of an invalid type or if
the same key is provided in different capitalizations
(regardless of case).
Defines standard constants for the Feature specification.
The name of the implementation capability for the Feature specification.
A Feature Model Extension. Extensions can contain either Text, JSON or a list of Artifacts.
Extensions are of one of the following kinds:
-
Mandatory: this extension must be processed by the runtime
-
Optional: this extension does not have to be processed by the runtime
-
Transient: this extension contains transient information such as caching data that is for optimization purposes. It may be changed or removed and is not part of the feature's identity.
Thread-safe
Consumers of this API must not implement this type
Get the Artifacts from this extension.
The Artifacts. The returned list is unmodifiable.
IllegalStateException
– If called on an extension which is not of
type ARTIFACTS.
Get the JSON from this extension.
The JSON.
IllegalStateException
– If called on an extension which is not of
type JSON.
Get the Text from this extension.
The lines of text. The returned list is unmodifiable.
IllegalStateException
– If called on an extension which is not of
type TEXT.
The kind of extension: optional, mandatory or transient.
A transient extension contains computed information which can be used as a cache to speed up operation.
The type of extension
A builder for Feature Model FeatureExtension objects.
Not Thread-safe
Consumers of this API must not implement this type
The artifact to add.
Add an Artifact to the extension. Can only be called for extensions of type FeatureExtension.Type.ARTIFACTS.
This builder.
The text to be added.
Add a line of text to the extension. Can only be called for extensions of type FeatureExtension.Type.TEXT.
This builder.
Build the Extension. Can only be called once on a builder. After calling this method the current builder instance cannot be used any more.
The Extension.
The JSON to be added.
Add JSON in String form to the extension. Can only be called for extensions of type FeatureExtension.Type.JSON.
This builder.
The Feature service is the primary entry point for interacting with the feature model.
Thread-safe
Consumers of this API must not implement this type
Get a factory which can be used to build feature model entities.
A builder factory.
The group ID (not null
, not empty).
The artifact ID (not null
, not empty).
The version (not null
, not empty).
Obtain an ID.
The ID.
The group ID (not null
, not empty).
The artifact ID (not null
, not empty).
The version (not null
, not empty).
The type (not null
, not empty).
Obtain an ID.
The ID.
The group ID (not null
, not empty).
The artifact ID (not null
, not empty).
The version (not null
, not empty).
The type (not null
, not empty).
The classifier (not null
, not empty).
Obtain an ID.
The ID.
The Maven Coordinates.
Obtain an ID from a Maven Coordinates formatted string. The supported syntax is as follows:
groupId ':' artifactId ( ':' type ( ':' classifier )? )? ':' version
the ID.
A Reader to the JSON input
Read a Feature from JSON
The Feature represented by the JSON
IOException
– When reading fails
ID used to denote an artifact. This could be a feature model, a bundle which is part of the feature model or some other artifact.
Artifact IDs follow the Maven convention of having:
-
A group ID
-
An artifact ID
-
A version
-
A type identifier (optional)
-
A classifier (optional)
Thread-safe
Consumers of this API must not implement this type
ID type for use with Features.
Feature Annotations Package Version 1.0.
This package contains annotations that can be used to require the Feature Service implementation.
Bundles should not normally need to import this package as the annotations are only used at build-time.
-
RequireFeatureService
- This annotation can be used to require the Feature implementation.
[1]JSON (JavaScript Object Notation) https://www.json.org
[2]JSMin (The JavaScript Minifier) https://www.crockford.com/javascript/jsmin.html
[3]Apache Maven Pom Reference https://maven.apache.org/pom.html