Pierre Villard created NIFI-8050:
------------------------------------

             Summary: Custom Groovy writer breaks during upgrade
                 Key: NIFI-8050
                 URL: https://issues.apache.org/jira/browse/NIFI-8050
             Project: Apache NiFi
          Issue Type: Bug
          Components: Core Framework
    Affects Versions: 1.12.1, 1.11.4
            Reporter: Pierre Villard


A couple of issues when upgrading NiFi and using a custom scripted writer with 
Groovy.

The scripted writer was something like: 
{code:java}
import ...

class GroovyRecordSetWriter implements RecordSetWriter {
    ...

    @Override
    WriteResult write(Record r) throws IOException {
        ...
    }

    @Override
    String getMimeType() { ... }

    @Override
    WriteResult write(final RecordSet rs) throws IOException {
        ...
    }

    public void beginRecordSet() throws IOException { ... }

    @Override
    public WriteResult finishRecordSet() throws IOException { ... }

    @Override
    public void close() throws IOException {}

    @Override
    public void flush() throws IOException {}
}

class GroovyRecordSetWriterFactory extends AbstractControllerService implements 
RecordSetWriterFactory {
    @Override
    RecordSchema getSchema(Map<String, String> variables, RecordSchema 
readSchema) throws SchemaNotFoundException, IOException {
       null
    }

    @Override
    RecordSetWriter createWriter(ComponentLog logger, RecordSchema schema, 
OutputStream out) throws SchemaNotFoundException, IOException {
       new GroovyRecordSetWriter(out)
    }
}

writer = new GroovyRecordSetWriterFactory()
{code}
With NIFI-6318 we changed a method in the interface RecordSetWriterFactory.

When using the above code in NiFi 1.9.2, it works fine but after an upgrade on 
1.11.4, this breaks. The Controller Service, when enabled, is throwing the 
below message:
{quote}Can't have an abstract method in a non-abstract class. The class 
'GroovyRecordSetWriterFactory' must be declared abstract or the method 
'org.apache.nifi.serialization.RecordSetWriter 
createWriter(org.apache.nifi.logging.ComponentLog, 
org.apache.nifi.serialization.record.RecordSchema, java.io.OutputStream, 
java.util.Map)' must be implemented.
{quote}
However the controller service is successfully enabled and the processors 
referencing it can be started. When using the ConvertRecord processor with the 
problematic controller service, it will throw the below NPE:
{code:java}
2020-11-26 15:46:13,876 ERROR [Timer-Driven Process Thread-25] 
o.a.n.processors.standard.ConvertRecord 
ConvertRecord[id=8b5456ae-71dc-3bd3-d0c0-df50d196fc00] Failed to process 
StandardFlowFileRecord[uuid=adebfcf6-b449-4d01-90a7-0463930aade0,claim=StandardContentClaim
 [resourceClaim=StandardResourceClaim[id=1606401933295-1, container=default, 
section=1], offset=80, 
length=296],offset=0,name=adebfcf6-b449-4d01-90a7-0463930aade0,size=296]; will 
route to failure: java.lang.NullPointerException 
java.lang.NullPointerException: null at 
org.apache.nifi.processors.standard.AbstractRecordProcessor$1.process(AbstractRecordProcessor.java:151)
 at 
org.apache.nifi.controller.repository.StandardProcessSession.write(StandardProcessSession.java:2986)
 at 
org.apache.nifi.processors.standard.AbstractRecordProcessor.onTrigger(AbstractRecordProcessor.java:122)
 at 
org.apache.nifi.processor.AbstractProcessor.onTrigger(AbstractProcessor.java:27)
 at 
org.apache.nifi.controller.StandardProcessorNode.onTrigger(StandardProcessorNode.java:1173)
 at 
org.apache.nifi.controller.tasks.ConnectableTask.invoke(ConnectableTask.java:214)
 at 
org.apache.nifi.controller.scheduling.TimerDrivenSchedulingAgent$1.run(TimerDrivenSchedulingAgent.java:117)
 at org.apache.nifi.engine.FlowEngine$2.run(FlowEngine.java:110) at 
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at 
java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) at 
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
 at 
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
 at 
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
at 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
at java.lang.Thread.run(Thread.java:748)
{code}
The fix is quite simple, it's just required to add the proper implementation 
method in the Groovy script. Something like:
{code:java}
class GroovyRecordSetWriterFactory extends AbstractControllerService implements 
RecordSetWriterFactory {
    @Override
    RecordSchema getSchema(Map<String, String> variables, RecordSchema 
readSchema) throws SchemaNotFoundException, IOException {
       null
    }
    @Override
    RecordSetWriter createWriter(ComponentLog logger, RecordSchema schema, 
OutputStream out) throws SchemaNotFoundException, IOException {
       new GroovyRecordSetWriter(out)
    }
    @Override
    RecordSetWriter createWriter(ComponentLog logger, RecordSchema schema, 
OutputStream out, Map<String, String> variables) throws 
SchemaNotFoundException, IOException {
       return createWriter(logger, schema, out)
    }
}
{code}
However, there are two things to improve:
 * if possible the controller service should not be successfully enabled - 
throwing a bulletin is nice but not enough in some environments where flow 
deployment is completely automated without the access to the UI
 * the NPE should be handled properly to provide a more meaningful message



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to