mirror of https://github.com/apache/nifi.git
NIFI-3761
- adjust testFullyDocumentedProcessor to correctly eval to false when needed - Introduce the ability to deprecate a component - Allow documentation to inform user about deprecation of a component - This closes #1718 NIFI-391 - Add set/getDeprecationReson to DocumentedDTO and use it within DtoFactory'
This commit is contained in:
parent
3353865ce9
commit
d092551211
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.annotation.documentation;
|
||||
|
||||
import org.apache.nifi.components.ConfigurableComponent;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that can be applied to a {@link org.apache.nifi.processor.Processor Processor},
|
||||
* {@link org.apache.nifi.controller.ControllerService ControllerService}, or
|
||||
* {@link org.apache.nifi.reporting.ReportingTask ReportingTask} in order to
|
||||
* warn about the deprecation of the component. The deprecation warning is informational only
|
||||
* and doesn't affect the processor run time behavior in any way
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface DeprecationNotice {
|
||||
Class<? extends ConfigurableComponent>[] value() default {};
|
||||
|
||||
String[] classNames() default {};
|
||||
|
||||
String reason() default "";
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ public class ControllerServiceDTO extends ComponentDTO {
|
|||
private String state;
|
||||
private Boolean persistsState;
|
||||
private Boolean restricted;
|
||||
private Boolean deprecated;
|
||||
private Boolean isExtensionMissing;
|
||||
private Boolean multipleVersionsAvailable;
|
||||
|
||||
|
@ -154,6 +155,20 @@ public class ControllerServiceDTO extends ComponentDTO {
|
|||
this.restricted = restricted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the controller service has been deprecated.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "Whether the ontroller service has been deprecated."
|
||||
)
|
||||
public Boolean getDeprecated() {
|
||||
return deprecated;
|
||||
}
|
||||
|
||||
public void setDeprecated(Boolean deprecated) {
|
||||
this.deprecated= deprecated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the underlying extension is missing
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,7 @@ public class DocumentedTypeDTO {
|
|||
private List<ControllerServiceApiDTO> controllerServiceApis;
|
||||
private String description;
|
||||
private String usageRestriction;
|
||||
private String deprecationReason;
|
||||
private Set<String> tags;
|
||||
|
||||
/**
|
||||
|
@ -64,6 +65,21 @@ public class DocumentedTypeDTO {
|
|||
this.usageRestriction = usageRestriction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An optional description of why the usage of this component is deprecated
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The description of why the usage of this component is restricted."
|
||||
)
|
||||
public String getDeprecationReason() {
|
||||
return deprecationReason;
|
||||
}
|
||||
|
||||
public void setDeprecationReason(String deprecationReason) {
|
||||
this.deprecationReason = deprecationReason;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return The type is the fully-qualified name of a Java class
|
||||
*/
|
||||
|
|
|
@ -41,6 +41,7 @@ public class ProcessorDTO extends ComponentDTO {
|
|||
private Boolean supportsBatching;
|
||||
private Boolean persistsState;
|
||||
private Boolean restricted;
|
||||
private Boolean deprecated;
|
||||
private Boolean isExtensionMissing;
|
||||
private Boolean multipleVersionsAvailable;
|
||||
private String inputRequirement;
|
||||
|
@ -200,6 +201,20 @@ public class ProcessorDTO extends ComponentDTO {
|
|||
this.restricted = restricted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the processor has been deprecated.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "Whether the processor has been deprecated."
|
||||
)
|
||||
public Boolean getDeprecated() {
|
||||
return deprecated;
|
||||
}
|
||||
|
||||
public void setDeprecated(Boolean deprecated) {
|
||||
this.deprecated = deprecated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the input requirement of this processor
|
||||
*/
|
||||
|
|
|
@ -35,6 +35,7 @@ public class ReportingTaskDTO extends ComponentDTO {
|
|||
private String comments;
|
||||
private Boolean persistsState;
|
||||
private Boolean restricted;
|
||||
private Boolean deprecated;
|
||||
private Boolean isExtensionMissing;
|
||||
private Boolean multipleVersionsAvailable;
|
||||
|
||||
|
@ -153,6 +154,20 @@ public class ReportingTaskDTO extends ComponentDTO {
|
|||
this.restricted = restricted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the reporting task has been deprecated.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "Whether the reporting task has been deprecated."
|
||||
)
|
||||
public Boolean getDeprecated() {
|
||||
return deprecated;
|
||||
}
|
||||
|
||||
public void setDeprecated(Boolean deprecated) {
|
||||
this.deprecated= deprecated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the underlying extension is missing
|
||||
*/
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.apache.nifi.annotation.behavior.DynamicProperty;
|
|||
import org.apache.nifi.annotation.behavior.Restricted;
|
||||
import org.apache.nifi.annotation.behavior.Stateful;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.documentation.SeeAlso;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
|
@ -31,6 +32,7 @@ import org.apache.nifi.components.PropertyDescriptor;
|
|||
import org.apache.nifi.controller.ControllerService;
|
||||
import org.apache.nifi.documentation.DocumentationWriter;
|
||||
import org.apache.nifi.nar.ExtensionManager;
|
||||
import org.apache.nifi.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -136,6 +138,7 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
throws XMLStreamException {
|
||||
xmlStreamWriter.writeStartElement("body");
|
||||
writeHeader(configurableComponent, xmlStreamWriter);
|
||||
writeDeprecationWarning(configurableComponent, xmlStreamWriter);
|
||||
writeDescription(configurableComponent, xmlStreamWriter, hasAdditionalDetails);
|
||||
writeTags(configurableComponent, xmlStreamWriter);
|
||||
writeProperties(configurableComponent, xmlStreamWriter);
|
||||
|
@ -216,6 +219,50 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a warning about the deprecation of a component.
|
||||
*
|
||||
* @param configurableComponent the component to describe
|
||||
* @param xmlStreamWriter the stream writer
|
||||
* @throws XMLStreamException thrown if there was a problem writing to the
|
||||
* XML stream
|
||||
*/
|
||||
private void writeDeprecationWarning(final ConfigurableComponent configurableComponent,
|
||||
final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
|
||||
final DeprecationNotice deprecationNotice = configurableComponent.getClass().getAnnotation(DeprecationNotice.class);
|
||||
if (deprecationNotice != null) {
|
||||
xmlStreamWriter.writeStartElement("h2");
|
||||
xmlStreamWriter.writeCharacters("Deprecation notice: ");
|
||||
xmlStreamWriter.writeEndElement();
|
||||
xmlStreamWriter.writeStartElement("p");
|
||||
|
||||
xmlStreamWriter.writeCharacters("");
|
||||
if (!StringUtils.isEmpty(deprecationNotice.reason())) {
|
||||
xmlStreamWriter.writeCharacters(deprecationNotice.reason());
|
||||
} else {
|
||||
// Write a default note
|
||||
xmlStreamWriter.writeCharacters("Please be aware this processor is deprecated and may be removed in " +
|
||||
"the near future.");
|
||||
}
|
||||
xmlStreamWriter.writeEndElement();
|
||||
xmlStreamWriter.writeStartElement("p");
|
||||
xmlStreamWriter.writeCharacters("Please consider using one the following alternatives: ");
|
||||
|
||||
|
||||
Class<? extends ConfigurableComponent>[] componentNames = deprecationNotice.value();
|
||||
String[] classNames = deprecationNotice.classNames();
|
||||
|
||||
if (componentNames.length > 0 || classNames.length > 0) {
|
||||
// Write alternatives
|
||||
iterateAndLinkComponents(xmlStreamWriter, componentNames, classNames, ",");
|
||||
} else {
|
||||
xmlStreamWriter.writeCharacters("No alternative components suggested.");
|
||||
}
|
||||
|
||||
xmlStreamWriter.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the list of components that may be linked from this component.
|
||||
*
|
||||
|
@ -229,43 +276,16 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
if (seeAlso != null) {
|
||||
writeSimpleElement(xmlStreamWriter, "h3", "See Also:");
|
||||
xmlStreamWriter.writeStartElement("p");
|
||||
int index = 0;
|
||||
for (final Class<? extends ConfigurableComponent> linkedComponent : seeAlso.value()) {
|
||||
if (index != 0) {
|
||||
xmlStreamWriter.writeCharacters(", ");
|
||||
}
|
||||
|
||||
writeLinkForComponent(xmlStreamWriter, linkedComponent);
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
for (final String linkedComponent : seeAlso.classNames()) {
|
||||
if (index != 0) {
|
||||
xmlStreamWriter.writeCharacters(", ");
|
||||
}
|
||||
|
||||
final List<Bundle> linkedComponentBundles = ExtensionManager.getBundles(linkedComponent);
|
||||
|
||||
if (linkedComponentBundles != null && linkedComponentBundles.size() > 0) {
|
||||
final Bundle firstBundle = linkedComponentBundles.get(0);
|
||||
final BundleCoordinate firstCoordinate = firstBundle.getBundleDetails().getCoordinate();
|
||||
|
||||
final String group = firstCoordinate.getGroup();
|
||||
final String id = firstCoordinate.getId();
|
||||
final String version = firstCoordinate.getVersion();
|
||||
|
||||
final String link = "/nifi-docs/components/" + group + "/" + id + "/" + version + "/" + linkedComponent + "/index.html";
|
||||
|
||||
final int indexOfLastPeriod = linkedComponent.lastIndexOf(".") + 1;
|
||||
|
||||
writeLink(xmlStreamWriter, linkedComponent.substring(indexOfLastPeriod), link);
|
||||
|
||||
++index;
|
||||
Class<? extends ConfigurableComponent>[] componentNames = seeAlso.value();
|
||||
String[] classNames = seeAlso.classNames();
|
||||
if (componentNames.length > 0 || classNames.length > 0) {
|
||||
// Write alternatives
|
||||
iterateAndLinkComponents(xmlStreamWriter, componentNames, classNames, ", ");
|
||||
} else {
|
||||
LOGGER.warn("Could not link to {} because no bundles were found", new Object[] {linkedComponent});
|
||||
}
|
||||
xmlStreamWriter.writeCharacters("No tags provided.");
|
||||
}
|
||||
|
||||
xmlStreamWriter.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
@ -295,7 +315,7 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
final String tagString = join(tags.value(), ", ");
|
||||
xmlStreamWriter.writeCharacters(tagString);
|
||||
} else {
|
||||
xmlStreamWriter.writeCharacters("None.");
|
||||
xmlStreamWriter.writeCharacters("No tags provided.");
|
||||
}
|
||||
xmlStreamWriter.writeEndElement();
|
||||
}
|
||||
|
@ -581,15 +601,16 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
xmlStreamWriter.writeEmptyElement("br");
|
||||
xmlStreamWriter.writeCharacters(controllerServiceClass.getSimpleName());
|
||||
|
||||
final List<Class<? extends ControllerService>> implementations = lookupControllerServiceImpls(controllerServiceClass);
|
||||
final List<Class<? extends ControllerService>> implementationList = lookupControllerServiceImpls(controllerServiceClass);
|
||||
|
||||
// Convert it into an array before proceeding
|
||||
Class<? extends ControllerService>[] implementations = implementationList.stream().toArray(Class[]::new);
|
||||
|
||||
xmlStreamWriter.writeEmptyElement("br");
|
||||
if (implementations.size() > 0) {
|
||||
final String title = implementations.size() > 1 ? "Implementations: " : "Implementation:";
|
||||
if (implementations.length > 0) {
|
||||
final String title = implementations.length > 1 ? "Implementations: " : "Implementation:";
|
||||
writeSimpleElement(xmlStreamWriter, "strong", title);
|
||||
for (int i = 0; i < implementations.size(); i++) {
|
||||
xmlStreamWriter.writeEmptyElement("br");
|
||||
writeLinkForComponent(xmlStreamWriter, implementations.get(i));
|
||||
}
|
||||
iterateAndLinkComponents(xmlStreamWriter, implementations, null, "<br>");
|
||||
} else {
|
||||
xmlStreamWriter.writeCharacters("No implementations found.");
|
||||
}
|
||||
|
@ -673,32 +694,6 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
xmlStreamWriter.writeEndElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a link to another configurable component
|
||||
*
|
||||
* @param xmlStreamWriter the xml stream writer
|
||||
* @param clazz the configurable component to link to
|
||||
* @throws XMLStreamException thrown if there is a problem writing the XML
|
||||
*/
|
||||
protected void writeLinkForComponent(final XMLStreamWriter xmlStreamWriter, final Class<?> clazz)
|
||||
throws XMLStreamException {
|
||||
final String linkedComponentName = clazz.getName();
|
||||
final List<Bundle> linkedComponentBundles = ExtensionManager.getBundles(linkedComponentName);
|
||||
|
||||
if (linkedComponentBundles != null && linkedComponentBundles.size() > 0) {
|
||||
final Bundle firstLinkedComponentBundle = linkedComponentBundles.get(0);
|
||||
final BundleCoordinate coordinate = firstLinkedComponentBundle.getBundleDetails().getCoordinate();
|
||||
|
||||
final String group = coordinate.getGroup();
|
||||
final String id = coordinate.getId();
|
||||
final String version = coordinate.getVersion();
|
||||
|
||||
writeLink(xmlStreamWriter, clazz.getSimpleName(), "/nifi-docs/components/" + group + "/" + id + "/" + version + "/" + clazz.getCanonicalName() + "/index.html");
|
||||
} else {
|
||||
LOGGER.warn("Could not link to {} because no bundles were found", new Object[] {linkedComponentName});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link ExtensionManager} to discover any {@link ControllerService} implementations that implement a specific
|
||||
* ControllerService API.
|
||||
|
@ -724,4 +719,94 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
|
|||
|
||||
return implementations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a link to another configurable component
|
||||
*
|
||||
* @param xmlStreamWriter the xml stream writer
|
||||
* @param linkedComponents the array of configurable component to link to
|
||||
* @param classNames the array of class names in string format to link to
|
||||
* @param separator a separator used to split the values (in case more than 1. If the separator is enclosed in
|
||||
* between "<" and ">" (.e.g "<br>" it is treated as a tag and written to the xmlStreamWriter as an
|
||||
* empty tag
|
||||
* @throws XMLStreamException thrown if there is a problem writing the XML
|
||||
*/
|
||||
protected void iterateAndLinkComponents(final XMLStreamWriter xmlStreamWriter, final Class<? extends ConfigurableComponent>[] linkedComponents, String[] classNames, String separator)
|
||||
throws XMLStreamException {
|
||||
|
||||
// Treat the the possible separators
|
||||
boolean separatorIsElement;
|
||||
|
||||
if (separator.startsWith("<") && separator.endsWith(">")) {
|
||||
separatorIsElement = true;
|
||||
} else {
|
||||
separatorIsElement = false;
|
||||
}
|
||||
// Whatever the result, strip the possible < and > characters
|
||||
separator = separator.replaceAll("\\<([^>]*)>","$1");
|
||||
|
||||
int index = 0;
|
||||
for (final Class<? extends ConfigurableComponent> linkedComponent : linkedComponents ) {
|
||||
final String linkedComponentName = linkedComponent.getName();
|
||||
final List<Bundle> linkedComponentBundles = ExtensionManager.getBundles(linkedComponentName);
|
||||
if (linkedComponentBundles != null && linkedComponentBundles.size() > 0) {
|
||||
final Bundle firstLinkedComponentBundle = linkedComponentBundles.get(0);
|
||||
final BundleCoordinate coordinate = firstLinkedComponentBundle.getBundleDetails().getCoordinate();
|
||||
|
||||
final String group = coordinate.getGroup();
|
||||
final String id = coordinate.getId();
|
||||
final String version = coordinate.getVersion();
|
||||
|
||||
|
||||
|
||||
if (index != 0) {
|
||||
if (separatorIsElement) {
|
||||
xmlStreamWriter.writeEmptyElement(separator);
|
||||
} else {
|
||||
xmlStreamWriter.writeCharacters(separator);
|
||||
}
|
||||
}
|
||||
writeLink(xmlStreamWriter, linkedComponent.getSimpleName(), "/nifi-docs/components/" + group + "/" + id + "/" + version + "/" + linkedComponent.getCanonicalName() + "/index.html");
|
||||
|
||||
++index;
|
||||
} else {
|
||||
LOGGER.warn("Could not link to {} because no bundles were found", new Object[] {linkedComponentName});
|
||||
}
|
||||
}
|
||||
|
||||
if (classNames!= null) {
|
||||
for (final String className : classNames) {
|
||||
if (index != 0) {
|
||||
if (separatorIsElement) {
|
||||
xmlStreamWriter.writeEmptyElement(separator);
|
||||
} else {
|
||||
xmlStreamWriter.writeCharacters(separator);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Bundle> linkedComponentBundles = ExtensionManager.getBundles(className);
|
||||
|
||||
if (linkedComponentBundles != null && linkedComponentBundles.size() > 0) {
|
||||
final Bundle firstBundle = linkedComponentBundles.get(0);
|
||||
final BundleCoordinate firstCoordinate = firstBundle.getBundleDetails().getCoordinate();
|
||||
|
||||
final String group = firstCoordinate.getGroup();
|
||||
final String id = firstCoordinate.getId();
|
||||
final String version = firstCoordinate.getVersion();
|
||||
|
||||
final String link = "/nifi-docs/components/" + group + "/" + id + "/" + version + "/" + className + "/index.html";
|
||||
|
||||
final int indexOfLastPeriod = className.lastIndexOf(".") + 1;
|
||||
|
||||
writeLink(xmlStreamWriter, className.substring(indexOfLastPeriod), link);
|
||||
|
||||
++index;
|
||||
} else {
|
||||
LOGGER.warn("Could not link to {} because no bundles were found", new Object[] {className});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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.documentation.example;
|
||||
|
||||
import org.apache.nifi.annotation.behavior.DynamicProperty;
|
||||
import org.apache.nifi.annotation.behavior.DynamicRelationship;
|
||||
import org.apache.nifi.annotation.behavior.ReadsAttribute;
|
||||
import org.apache.nifi.annotation.behavior.Restricted;
|
||||
import org.apache.nifi.annotation.behavior.Stateful;
|
||||
import org.apache.nifi.annotation.behavior.WritesAttribute;
|
||||
import org.apache.nifi.annotation.behavior.WritesAttributes;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.documentation.SeeAlso;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.annotation.lifecycle.OnRemoved;
|
||||
import org.apache.nifi.annotation.lifecycle.OnShutdown;
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.components.state.Scope;
|
||||
import org.apache.nifi.processor.AbstractProcessor;
|
||||
import org.apache.nifi.processor.ProcessContext;
|
||||
import org.apache.nifi.processor.ProcessSession;
|
||||
import org.apache.nifi.processor.ProcessorInitializationContext;
|
||||
import org.apache.nifi.processor.Relationship;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.processor.util.StandardValidators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Tags({"one", "two", "three"})
|
||||
@CapabilityDescription("This is a processor that is used to test documentation.")
|
||||
@WritesAttributes({
|
||||
@WritesAttribute(attribute = "first", description = "this is the first attribute i write"),
|
||||
@WritesAttribute(attribute = "second")})
|
||||
@ReadsAttribute(attribute = "incoming", description = "this specifies the format of the thing")
|
||||
@SeeAlso(value = {FullyDocumentedControllerService.class, FullyDocumentedReportingTask.class}, classNames = {"org.apache.nifi.processor.ExampleProcessor"})
|
||||
@DynamicProperty(name = "Relationship Name", supportsExpressionLanguage = true, value = "some XPath", description = "Routes FlowFiles to relationships based on XPath")
|
||||
@DynamicRelationship(name = "name from dynamic property", description = "all files that match the properties XPath")
|
||||
@Stateful(scopes = {Scope.CLUSTER, Scope.LOCAL}, description = "state management description")
|
||||
@Restricted("processor restriction description")
|
||||
@DeprecationNotice({FullyDocumentedProcessor.class, FullyDocumentedReportingTask.class})
|
||||
public class DeprecatedProcessor extends AbstractProcessor {
|
||||
|
||||
public static final PropertyDescriptor DIRECTORY = new PropertyDescriptor.Builder().name("Input Directory")
|
||||
.description("The input directory from which to pull files").required(true)
|
||||
.addValidator(StandardValidators.createDirectoryExistsValidator(true, false))
|
||||
.expressionLanguageSupported(true).build();
|
||||
|
||||
public static final PropertyDescriptor RECURSE = new PropertyDescriptor.Builder().name("Recurse Subdirectories")
|
||||
.description("Indicates whether or not to pull files from subdirectories").required(true)
|
||||
.allowableValues(
|
||||
new AllowableValue("true", "true", "Should pull from sub directories"),
|
||||
new AllowableValue("false", "false", "Should not pull from sub directories")
|
||||
).defaultValue("true").build();
|
||||
|
||||
public static final PropertyDescriptor POLLING_INTERVAL = new PropertyDescriptor.Builder().name("Polling Interval")
|
||||
.description("Indicates how long to wait before performing a directory listing").required(true)
|
||||
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR).defaultValue("0 sec").build();
|
||||
|
||||
public static final PropertyDescriptor OPTIONAL_PROPERTY = new PropertyDescriptor.Builder()
|
||||
.name("Optional Property").description("This is a property you can use or not").required(false).build();
|
||||
|
||||
public static final PropertyDescriptor TYPE_PROPERTY = new PropertyDescriptor.Builder()
|
||||
.name("Type")
|
||||
.description("This is the type of something that you can choose. It has several possible values")
|
||||
.allowableValues("yes", "no", "maybe", "possibly", "not likely", "longer option name")
|
||||
.required(true).build();
|
||||
|
||||
public static final PropertyDescriptor SERVICE_PROPERTY = new PropertyDescriptor.Builder()
|
||||
.name("Controller Service").description("This is the controller service to use to do things")
|
||||
.identifiesControllerService(SampleService.class).required(true).build();
|
||||
|
||||
public static final Relationship REL_SUCCESS = new Relationship.Builder().name("success")
|
||||
.description("Successful files").build();
|
||||
public static final Relationship REL_FAILURE = new Relationship.Builder().name("failure")
|
||||
.description("Failing files").build();
|
||||
|
||||
private List<PropertyDescriptor> properties;
|
||||
private Set<Relationship> relationships;
|
||||
|
||||
private int onRemovedNoArgs = 0;
|
||||
private int onRemovedArgs = 0;
|
||||
|
||||
private int onShutdownNoArgs = 0;
|
||||
private int onShutdownArgs = 0;
|
||||
|
||||
@Override
|
||||
protected void init(ProcessorInitializationContext context) {
|
||||
final List<PropertyDescriptor> properties = new ArrayList<>();
|
||||
properties.add(DIRECTORY);
|
||||
properties.add(RECURSE);
|
||||
properties.add(POLLING_INTERVAL);
|
||||
properties.add(OPTIONAL_PROPERTY);
|
||||
properties.add(TYPE_PROPERTY);
|
||||
properties.add(SERVICE_PROPERTY);
|
||||
this.properties = Collections.unmodifiableList(properties);
|
||||
|
||||
final Set<Relationship> relationships = new HashSet<>();
|
||||
relationships.add(REL_SUCCESS);
|
||||
relationships.add(REL_FAILURE);
|
||||
this.relationships = Collections.unmodifiableSet(relationships);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Relationship> getRelationships() {
|
||||
return relationships;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(String propertyDescriptorName) {
|
||||
return new PropertyDescriptor.Builder().name(propertyDescriptorName)
|
||||
.description("This is a property you can use or not").dynamic(true).build();
|
||||
}
|
||||
|
||||
@OnRemoved
|
||||
public void onRemovedNoArgs() {
|
||||
onRemovedNoArgs++;
|
||||
}
|
||||
|
||||
@OnRemoved
|
||||
public void onRemovedArgs(ProcessContext context) {
|
||||
onRemovedArgs++;
|
||||
}
|
||||
|
||||
@OnShutdown
|
||||
public void onShutdownNoArgs() {
|
||||
onShutdownNoArgs++;
|
||||
}
|
||||
|
||||
@OnShutdown
|
||||
public void onShutdownArgs(ProcessContext context) {
|
||||
onShutdownArgs++;
|
||||
}
|
||||
|
||||
public int getOnRemovedNoArgs() {
|
||||
return onRemovedNoArgs;
|
||||
}
|
||||
|
||||
public int getOnRemovedArgs() {
|
||||
return onRemovedArgs;
|
||||
}
|
||||
|
||||
public int getOnShutdownNoArgs() {
|
||||
return onShutdownNoArgs;
|
||||
}
|
||||
|
||||
public int getOnShutdownArgs() {
|
||||
return onShutdownArgs;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.apache.nifi.documentation.html;
|
|||
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.documentation.DocumentationWriter;
|
||||
import org.apache.nifi.documentation.example.DeprecatedProcessor;
|
||||
import org.apache.nifi.documentation.example.FullyDocumentedProcessor;
|
||||
import org.apache.nifi.documentation.example.NakedProcessor;
|
||||
import org.apache.nifi.documentation.example.ProcessorWithLogger;
|
||||
|
@ -76,7 +77,8 @@ public class ProcessorDocumentationWriterTest {
|
|||
.value());
|
||||
assertNotContains(results, "This component has no required or optional properties.");
|
||||
assertNotContains(results, "No description provided.");
|
||||
assertNotContains(results, "No Tags provided.");
|
||||
|
||||
assertNotContains(results, "No tags provided.");
|
||||
assertNotContains(results, "Additional Details...");
|
||||
|
||||
// verify the right OnRemoved and OnShutdown methods were called
|
||||
|
@ -107,7 +109,7 @@ public class ProcessorDocumentationWriterTest {
|
|||
assertContains(results, "No description provided.");
|
||||
|
||||
// no tags
|
||||
assertContains(results, "None.");
|
||||
assertContains(results, "No tags provided.");
|
||||
|
||||
// properties
|
||||
assertContains(results, "This component has no required or optional properties.");
|
||||
|
@ -139,4 +141,63 @@ public class ProcessorDocumentationWriterTest {
|
|||
XmlValidator.assertXmlValid(results);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeprecatedProcessor() throws IOException {
|
||||
DeprecatedProcessor processor = new DeprecatedProcessor();
|
||||
ProcessorInitializer initializer = new ProcessorInitializer();
|
||||
initializer.initialize(processor);
|
||||
|
||||
DocumentationWriter writer = new HtmlProcessorDocumentationWriter();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
writer.write(processor, baos, false);
|
||||
initializer.teardown(processor);
|
||||
|
||||
String results = new String(baos.toByteArray());
|
||||
XmlValidator.assertXmlValid(results);
|
||||
|
||||
assertContains(results, DeprecatedProcessor.DIRECTORY.getDisplayName());
|
||||
assertContains(results, DeprecatedProcessor.DIRECTORY.getDescription());
|
||||
assertContains(results, DeprecatedProcessor.OPTIONAL_PROPERTY.getDisplayName());
|
||||
assertContains(results, DeprecatedProcessor.OPTIONAL_PROPERTY.getDescription());
|
||||
assertContains(results, DeprecatedProcessor.POLLING_INTERVAL.getDisplayName());
|
||||
assertContains(results, DeprecatedProcessor.POLLING_INTERVAL.getDescription());
|
||||
assertContains(results, DeprecatedProcessor.POLLING_INTERVAL.getDefaultValue());
|
||||
assertContains(results, DeprecatedProcessor.RECURSE.getDisplayName());
|
||||
assertContains(results, DeprecatedProcessor.RECURSE.getDescription());
|
||||
|
||||
assertContains(results, DeprecatedProcessor.REL_SUCCESS.getName());
|
||||
assertContains(results, DeprecatedProcessor.REL_SUCCESS.getDescription());
|
||||
assertContains(results, DeprecatedProcessor.REL_FAILURE.getName());
|
||||
assertContains(results, DeprecatedProcessor.REL_FAILURE.getDescription());
|
||||
assertContains(results, "Controller Service API: ");
|
||||
assertContains(results, "SampleService");
|
||||
|
||||
assertContains(results, "CLUSTER, LOCAL");
|
||||
assertContains(results, "state management description");
|
||||
|
||||
assertContains(results, "processor restriction description");
|
||||
|
||||
assertNotContains(results, "iconSecure.png");
|
||||
assertContains(results, DeprecatedProcessor.class.getAnnotation(CapabilityDescription.class)
|
||||
.value());
|
||||
|
||||
// Check for the existence of deprecation notice
|
||||
assertContains(results, "Deprecation notice: ");
|
||||
// assertContains(results, DeprecatedProcessor.class.getAnnotation(DeprecationNotice.class.));
|
||||
|
||||
assertNotContains(results, "This component has no required or optional properties.");
|
||||
assertNotContains(results, "No description provided.");
|
||||
assertNotContains(results, "No tags provided.");
|
||||
assertNotContains(results, "Additional Details...");
|
||||
|
||||
// verify the right OnRemoved and OnShutdown methods were called
|
||||
Assert.assertEquals(0, processor.getOnRemovedArgs());
|
||||
Assert.assertEquals(0, processor.getOnRemovedNoArgs());
|
||||
|
||||
Assert.assertEquals(1, processor.getOnShutdownArgs());
|
||||
Assert.assertEquals(1, processor.getOnShutdownNoArgs());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,11 @@ public interface ConfiguredComponent extends ComponentAuthorizable {
|
|||
*/
|
||||
boolean isRestricted();
|
||||
|
||||
/**
|
||||
* @return whether or not the underlying implementation is deprecated
|
||||
*/
|
||||
boolean isDeprecated();
|
||||
|
||||
@Override
|
||||
default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
|
||||
// if this is a modification request and the reporting task is restricted ensure the user has elevated privileges. if this
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.nifi.annotation.behavior.TriggerWhenAnyDestinationAvailable;
|
|||
import org.apache.nifi.annotation.behavior.TriggerWhenEmpty;
|
||||
import org.apache.nifi.annotation.configuration.DefaultSchedule;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.lifecycle.OnScheduled;
|
||||
import org.apache.nifi.annotation.lifecycle.OnStopped;
|
||||
import org.apache.nifi.annotation.lifecycle.OnUnscheduled;
|
||||
|
@ -239,6 +240,12 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable
|
|||
return getProcessor().getClass().isAnnotationPresent(Restricted.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeprecated() {
|
||||
return getProcessor().getClass().isAnnotationPresent(DeprecationNotice.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides and opportunity to retain information about this particular
|
||||
* processor instance
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.nifi.controller.reporting;
|
||||
|
||||
import org.apache.nifi.annotation.behavior.Restricted;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
|
@ -66,6 +67,11 @@ public class StandardReportingTaskNode extends AbstractReportingTaskNode impleme
|
|||
return getReportingTask().getClass().isAnnotationPresent(Restricted.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeprecated() {
|
||||
return getReportingTask().getClass().isAnnotationPresent(DeprecationNotice.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportingContext getReportingContext() {
|
||||
return new StandardReportingContext(flowController, flowController.getBulletinRepository(), getProperties(), flowController, getReportingTask(), getVariableRegistry());
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.nifi.controller.service;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.annotation.behavior.Restricted;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.lifecycle.OnDisabled;
|
||||
import org.apache.nifi.annotation.lifecycle.OnEnabled;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
|
@ -146,6 +147,11 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
|
|||
return getControllerServiceImplementation().getClass().isAnnotationPresent(Restricted.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeprecated() {
|
||||
return getControllerServiceImplementation().getClass().isAnnotationPresent(DeprecationNotice.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerService getControllerServiceImplementation() {
|
||||
return controllerServiceHolder.get().getImplementation();
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.nifi.action.details.PurgeDetails;
|
|||
import org.apache.nifi.annotation.behavior.Restricted;
|
||||
import org.apache.nifi.annotation.behavior.Stateful;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.AccessPolicy;
|
||||
|
@ -1254,6 +1255,7 @@ public final class DtoFactory {
|
|||
dto.setComments(reportingTaskNode.getComments());
|
||||
dto.setPersistsState(reportingTaskNode.getReportingTask().getClass().isAnnotationPresent(Stateful.class));
|
||||
dto.setRestricted(reportingTaskNode.isRestricted());
|
||||
dto.setDeprecated(reportingTaskNode.isDeprecated());
|
||||
dto.setExtensionMissing(reportingTaskNode.isExtensionMissing());
|
||||
dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
|
||||
|
||||
|
@ -1334,6 +1336,7 @@ public final class DtoFactory {
|
|||
dto.setComments(controllerServiceNode.getComments());
|
||||
dto.setPersistsState(controllerServiceNode.getControllerServiceImplementation().getClass().isAnnotationPresent(Stateful.class));
|
||||
dto.setRestricted(controllerServiceNode.isRestricted());
|
||||
dto.setDeprecated(controllerServiceNode.isDeprecated());
|
||||
dto.setExtensionMissing(controllerServiceNode.isExtensionMissing());
|
||||
dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
|
||||
|
||||
|
@ -2062,6 +2065,11 @@ public final class DtoFactory {
|
|||
return restriction == null ? null : restriction.value();
|
||||
}
|
||||
|
||||
private String getDeprecationReason(final Class<?> cls) {
|
||||
final DeprecationNotice deprecationNotice = cls.getAnnotation(DeprecationNotice.class);
|
||||
return deprecationNotice == null ? null : deprecationNotice.reason();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the capability description from the specified class.
|
||||
*/
|
||||
|
@ -2164,6 +2172,7 @@ public final class DtoFactory {
|
|||
dto.setControllerServiceApis(createControllerServiceApiDto(cls));
|
||||
dto.setDescription(getCapabilityDescription(cls));
|
||||
dto.setUsageRestriction(getUsageRestriction(cls));
|
||||
dto.setDeprecationReason(getDeprecationReason(cls));
|
||||
dto.setTags(getTags(cls));
|
||||
types.add(dto);
|
||||
}
|
||||
|
@ -2213,6 +2222,7 @@ public final class DtoFactory {
|
|||
dto.setInputRequirement(node.getInputRequirement().name());
|
||||
dto.setPersistsState(node.getProcessor().getClass().isAnnotationPresent(Stateful.class));
|
||||
dto.setRestricted(node.isRestricted());
|
||||
dto.setDeprecated(node.isDeprecated());
|
||||
dto.setExtensionMissing(node.isExtensionMissing());
|
||||
dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue