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; }
}

Reply via email to