blob: 63793533331822a11c7c3d247311d70a8e4683a3 [file] [log] [blame]
/*
* 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
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" 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>
* Components to implement a Service Provider Interface implementation for Sentry.
* </p>
* <p>
* These classes are a way to simplify loading of different "plugin" service implementations
* within the sentry. It allows for the easy creation of separate <strong>Services</strong> and
* allows for multiple implementations of that service to be defined and easily loaded. It is able
* to get all implementations or a single one depending on configuration and need.
* </p>
* <p>
* The Sentry API module takes advantage of the
* <a href="https://docs.oracle.com/javase/tutorial/ext/basics/spi.html">Java Service Loader</a>
* which was introduced in Java 7. It uses the Factory pattern in order to get concrete instances
* of Services and allow for custom initialization of those concrete instances.
* </p>
*
* <h2>Create an Service Instance:</h2>
* <dl>
* <dt>Create concert classes for the Services Provider and ProviderFactory interfaces.</dt>
* <dd>
* <p>In order to create a service instance you need to create instances of the services
* Provider and ProviderFactory interfaces. These should contain all of the necessary
* functions defined by the interface.</p>
*
* <p>The ProviderFactory instance needs to have the getId() functioned defined to return a unique
* name for the Service Provider instance so that it can be looked up by that name.</p>
*
* <p>The create() function of the ProviderFactory will return a concert instance of the Provider</p>
* <strong>Sample src/main/resources/META-INF/services/org.apache.sentry.spi.FooSomeProvider file</strong>
* <pre>{@code
* package org.apache.sentry.fake;
*
* public class FooSomeProvider implements SomeProvider {
* public void doSomething(){
* ... does something ...
* }
* }
* }</pre>
*
* <strong>Sample src/main/resources/META-INF/services/org.apache.sentry.spi.FooSomeProviderFactory file</strong>
* <pre>{@code
* package org.apache.sentry.fake;
*
* public class FooSomeProviderFactory implements SomeProviderFactory {
* @Override
* public String getId() {
* return "foo"
* }
*
* @Override
* public SomeProvider create() {
* return new SomeProvider();
* }
* }
* }</pre>
* </dd>
* <dt>Create an entry for the ProviderFactory instance in the service configuration file for
* the SPI</dt>
* <dd>
* <p>The service configuration file is placed in the META-INF/services directory of a jar and
* is named after the ProviderFactory instance of the SPI.</p>
* <strong>Sample src/main/resources/META-INF/services/org.apache.sentry.fake.SomeProviderFactory file</strong>
* <pre>{@code
* org.apache.sentry.fake.FooSomeProviderFactory
* }</pre>
* </dd>
* <dt>Load the Service instance with the ProviderManager</dt>
* <dd>
* <p>You can then load the service from code with the ProviderManager. Either all service
* providers or an individual one.</p>
* <pre>{@code
* # Loads instances of all SomeProviderFactory defined
* List<SomeProviderFactory> = ProviderManager.getInstance().load("some-spi");
*
* # Loads only the "foo" instance of the SomeProviderFactory
* SomeProviderFactory someProviderFactory = ProviderManager.getInstance().load("some-spi", "foo");
* }</pre>
* </dd>
*
*
*
* <h2>How to Implement a New Service:</h2>
* <dl>
* <dt>Create an SPI implantation</dt>
* <dd>
* <p>
* You can create Service by implementing a concrete instance of the SPI interface.
* This interface will provider information about what interfaces the SPI will be looking for
* when loading instances. It requires the Provider and the ProviderFactory information as
* well as a unique name for the Service in the system.
* </p>
* <strong>Sample src/main/java/org/apache/sentry/fake/SomeSpi.java file</strong>
* <pre>{@code
* package org.apache.sentry.fake;
*
* public class SomeSpi implements Spi {
*
* @Override
* public String getName() {
* return "some-spi";
* }
*
* @Override
* public Class<? extends Provider> getProviderClass() {
* return SomeProvider.class;
* }
*
* @Override
* public Class<? extends ProviderFactory> getProviderFactoryClass() {
* return SomeProviderFactory.class;
* }
* }
* }</pre>
* <p>
* As well you must put an entry for the SPI concrete class in the
* <strong>META-INF/services/org.apache.sentry.spi.Spi</strong> service configuration file pointing to that instance.
* </p>
* <strong>Sample src/main/resources/META-INF/services/org.apache.sentry.spi.SomeSpi file</strong>
* <pre>{@code
* org.apache.sentry.fake.SomeSpi
* org.apache.sentry.fake.SomeOtherSpi
* }</pre>
* <dt>Create a the Provider and Provider Factory interfaces</dt>
* <dd>
* <p>You need to create the interfaces referenced in the the SPI class. These extend the
* Provider and ProviderFactory interfaces and can be customized to have the function definitions
* for how you want your service to operate.</p>
*
* <strong>Sample rc/main/java/org/apache/sentry/fake/SomeProvider.java file</strong>
* <pre>{@code
* package org.apache.sentry.fake;
*
* public interface SomeProvider extends Provider {
* void doSomething();
* }
*
* }</pre>
* <strong>Sample src/main/java/org/apache/sentry/fake/SomeProviderFactory.java file</strong>
* <pre>{@code
* package org.apache.sentry.fake;
*
* public interface SomeProviderFactory extends ProviderFactory<SomeProvider> {
* void init(SomeConfig config);
* }
* }</pre>
*
* </dd>
* </dl>
*
*
*
*/
package org.apache.sentry.spi;