I only put the code I want to execute in onTrigger(), I suspected it
would not fire there. I know that this isn't what processors do.
Configuration is a messy problem to solve when your downstreamers want
it made easy. This is supposed to be a solution that allows them to
remain in the NiFi UI and not have to run off doing harder things to
configure. I could put what I'm doing in /HumanReadables/ into a real,
running processor, but then, I would kind of have to add them to several
processors and I wanted to avoid the confusion that created.
Here's the code. Thanks.
import com.windofkeltia.constants.HumanReadableMappings;
...
@TriggerWhenEmpty
@SideEffectFree
@CapabilityDescription( "Dynamic properties can be created to specify
(or add to) static configuration"
+ " of key-value substitution pairs. See
additional details." )
public class HumanReadables extends AbstractProcessor
{
@Override
public void onTrigger( final ProcessContext context, final
ProcessSession session ) throws ProcessException
{
getLogger().info( "inside onTrigger()..." );
for( Map.Entry< PropertyDescriptor, String > entry :
context.getProperties().entrySet() )
{
PropertyDescriptor property = entry.getKey();
// do work here--maybe pre-wipe all key-value pairs if we're just
going to recreate them?
final String PROPERTY_NAME = property.getName();
final String PROPERTY_VALUE = entry.getValue();
logger.trace( "Processing configurable mappings titled \"" +
PROPERTY_NAME + "\"" );
try
{
harvestDynamicPropertyMappings( PROPERTY_VALUE );
}
catch( Exception e )
{
getLogger().debug( e.getMessage() );
}
}
}
protected static void harvestDynamicPropertyMappings( final String
PROPERTY_VALUE )
{
final String[] LINES = PROPERTY_VALUE.split( "\n" );
int lineNumber = 0;
if( LINES.length < 1 )
return;
final String WHICH_LIST = LINES[ 0 ];
for( final String VALUE_LINE : LINES )
{
char delimiter = VALUE_LINE.charAt( 0 );
int position = VALUE_LINE.indexOf( delimiter, 1 );
String key, value;
key = ( position < 0 ) ? VALUE_LINE.substring( 1 ) :
VALUE_LINE.substring( 1, position ).trim();
value = ( position > 0 ) ? VALUE_LINE.substring( position + 1
).trim() : "";
HumanReadableMappings.add( key, value );
}
}
@Override
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(
final String propertyDescriptorName )
{
return new PropertyDescriptor.Builder()
.required( false )
.name( propertyDescriptorName )
.addValidator(
StandardValidators.NON_EMPTY_VALIDATOR )
// or .addValidator( Validator.VALID )
if you do not wish it validated!
.dynamic( true )
.build();
}
private volatile Set< String > dynamicPropertyNames = new HashSet<>();
@Override
public void onPropertyModified( final PropertyDescriptor descriptor,
final String oldValue, final String newValue )
{
getLogger().info( oldValue + " -> " + newValue );
final Set< String > newDynamicPropertyNames = new HashSet<>(
dynamicPropertyNames );
if( isNull( newValue ) )
newDynamicPropertyNames.remove( descriptor.getName() );
else if( isNull( oldValue ) && descriptor.isDynamic() )
newDynamicPropertyNames.add( descriptor.getName() );
dynamicPropertyNames = Collections.unmodifiableSet(
newDynamicPropertyNames );
final Set< String > allDynamicProperties = dynamicPropertyNames;
}
@OnScheduled public void processProperties( final ProcessContext
context )
{
for( Map.Entry< PropertyDescriptor, String > entry :
context.getProperties().entrySet() )
{
PropertyDescriptor descriptor = entry.getKey();
if( descriptor.isDynamic() )
getLogger().debug( "Dynamic property named:\n " +
descriptor.getName()
+ ", value: " +
entry.getValue().replaceAll( "\n", " + " ) );
}
}
protected static final String DEFAULT_MAPPING_VALUE = ""
+ "|http://loinc.org |LOINC\n"
+ "|http://snomed.info/sct |SNOMED\n"
+ "|http://www.ama-assn.org/go/cpt |CPT\n"
+ "|http://aapc.org |CPT\n"
+ "|http://www.nlm.nih.gov/research/umls/rxnorm |RxNorm\n"
+ "|http://hl7.org/fhir/sid/ndc |NDC\n"
+ "|http://hl7.org/fhir/sid/icd-9-cm |ICD-9\n"
+ "|http://hl7.org/fhir/sid/icd-10 |ICD-10\n";
public static final PropertyDescriptor DEFAULT_MAPPINGS = new
PropertyDescriptor.Builder()
.name( "default mappings" )
.displayName( "Default mappings" )
.required( false )
.expressionLanguageSupported( ExpressionLanguageScope.NONE )
.defaultValue( DEFAULT_MAPPING_VALUE )
.addValidator( Validator.VALID )
.description( "These default mappings are already set up, but can
be changed. To erase completely,"
+ " merely clear the value of this property and
ignore it in favor of creating and"
+ " maintaining one or more of your own dynamic
properties.")
.build();
private List< PropertyDescriptor > properties;
@Override
public void init( final ProcessorInitializationContext context )
{
List< PropertyDescriptor > properties = new ArrayList<>();
properties.add( DEFAULT_MAPPINGS );
this.properties = Collections.unmodifiableList( properties );
}
@Override public List< PropertyDescriptor >
getSupportedPropertyDescriptors() { return properties; }
}