org.apache.nifi.controller.ControllerService Maven / Gradle / Ivy
/*
* 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.
*/
package org.apache.nifi.controller;
import org.apache.nifi.annotation.behavior.Stateful;
import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.flowanalysis.FlowAnalysisRule;
import org.apache.nifi.migration.PropertyConfiguration;
import org.apache.nifi.parameter.ParameterProvider;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.reporting.ReportingTask;
/**
*
* This interface provides a mechanism for creating services that are shared
* among all {@link Processor}s, {@link ReportingTask}s, {@link FlowAnalysisRule}s, {@link ParameterProvider}s and other
* {@code ControllerService}s.
*
*
*
* ControllerService
s are discovered using Java's
* ServiceLoader
mechanism. As a result, all implementations must
* follow these rules:
*
*
*
* - The implementation must implement this interface.
* - The implementation must have a file named
* org.apache.nifi.controller.ControllerService located within the jar's
*
META-INF/services
directory. This file contains a list of
* fully-qualified class names of all ControllerService
s in the
* jar, one-per-line.
* - The implementation must support a default constructor.
*
*
*
* All implementations of this interface must be thread-safe.
*
*
* Accessing Controller Services
*
* A ControllerService is accessible only through its interface. The framework
* provides access to a ControllerService through two different mechanisms:
*
* -
* A {@link PropertyDescriptor} can be created via the
* {@link PropertyDescriptor.Builder} after calling the
* {@link PropertyDescriptor.Builder#identifiesControllerService(Class) identifiesControllerService(Class)}
* method and then the ControllerService is accessed via
* {@link PropertyValue#asControllerService(Class)} method.
*
* For example:
*
*
* public static final PropertyDescriptor MY_PROPERTY = new PropertyDescriptor.Builder()
* .name("My Property")
* .description("Example Property")
* .identifiesControllerService( MyControllerServiceInterface.class )
* .build();
*
* ...
* public void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException {
* // Obtain the user-selected controller service
* final MyControllerServiceInterface service = context.getProperty(MY_PROPERTY).asControllerService( MyControllerServiceInterface.class );
* ...
* }
*
*
* - A Controller Service can be obtained via a
* {@link ControllerServiceLookup}. This lookup may be obtained, for example,
* from the {@link ProcessContext} that is provided to a {@link Processor}'s
* {@link Processor#onTrigger(ProcessContext, ProcessSessionFactory) onTrigger}
* method.
*
* For example:
*
*
*
* public void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException {
* final MyControllerServiceInterface service = (MyControllerServiceInterface) context.getControllerServiceLookup().getControllerService("service_identifier");
* }
*
*
*
*
* Defining a Controller Service
*
* Note in both of the examples above, that the Controller Service was accessed
* only by its interface, and this interface extends ControllerService. If we
* have an implementation named MyServiceImpl, for example, that implements
* MyControllerServiceInterface, we cannot, in either case, attempt to cast the
* ControllerService to the desired implementation. Doing so will result in a
* {@link ClassCastException}. This is by design and is done for the following
* reasons:
*
*
* -
* It is a good coding practice to implement such a service as an interface in
* general.
*
* -
* A Controller Service can be referenced from different NiFi Archives (NARs).
* This means that the Controller Service may be defined in one ClassLoader and
* referenced from another unrelated ClassLoader. In order to account for this,
* NiFi will change the current thread's ClassLoader as appropriate when
* entering the Controller Service's code and revert back to the previous
* ClassLoader after exiting the Controller Service's code.
*
*
*
* Controller Services and NARs
*
* Due to the fact that a Controller Service may be referenced from a different
* NAR than the one in which the implementation lives, it is crucial that both
* the Controller Service's interface and the code referencing the interface
* inherit from the same ClassLoader. This is accomplished by ensuring that the
* NAR that contains the Controller Service interface is the parent (or
* ancestor) of the NAR that references the Controller Service interface.
*
*
*
* Typically, this is done by creating a NAR structure as follows:
*
* + my-services-api-nar
* +--- service-X-implementation-nar
* +--- service-Y-implementation-nar
* +--- service-Z-implementation-nar
* +--- processor-A-nar
* +--- processor-B-nar
*
*
*
* In this case, the {@code MyControllerServiceInterface} interface, and any
* other Controller Service interfaces, will be defined in the
* {@code my-services-api-nar} NAR. Implementations are then encapsulated within
* the {@code service-X-implementation-nar},
* {@code service-Y-implementation-nar}, and
* {@code service-Z-implementation-nar} NARs. All Controller Services and all
* Processors defined in these NARs are able to reference any other Controller
* Services whose interfaces are provided in the {@code my-services-api-nar}
* NAR.
*
*
*
* For more information on NARs, see the NiFi Developer Guide.
*
*/
public interface ControllerService extends ConfigurableComponent {
/**
* Provides the Controller Service with access to objects that may be of use
* throughout the life of the service. This method will be called before any
* properties are set
*
* @param context of initialization
* @throws org.apache.nifi.reporting.InitializationException if unable to init
*/
void initialize(ControllerServiceInitializationContext context) throws InitializationException;
/**
* Indicates whether this controller service, configured with the given {@link ConfigurationContext}, stores state.
* @param context provides access to convenience methods for obtaining property values
* @return True if this controller service stores state
*/
default boolean isStateful(ConfigurationContext context) {
return this.getClass().isAnnotationPresent(Stateful.class);
}
/**
*
* Allows for the migration of an old property configuration to a new configuration. This allows the Controller Service to evolve over time,
* as it allows properties to be renamed, removed, or reconfigured.
*
*
*
* This method is called only when a Controller Service is restored from a previous configuration. For example, when NiFi is restarted and the
* flow is restored from disk, when a previously configured flow is imported (e.g., from a JSON file that was exported or a NiFi Registry),
* or when a node joins a cluster and inherits a flow that has a new Controller Service. Once called, the method will not be invoked again for this
* Controller Service until NiFi is restarted.
*
*
* @param config the current property configuration
*/
default void migrateProperties(PropertyConfiguration config) {
}
}