Eventually I find a way to make it work. The "init" method is triggered when 
the service instance is created before the property is configured/ready. I 
can't do data loading there.
I need to prepare another API to lazily load data from database, before any 
call of "getXXX(String key)". As it is multi-thread env, I need to use 
synchronized key to make it thread safe. See the following method:


private synchronized void reloadOnce() {
    if(this.dbcpService == null) {
        this.dbcpService = 
getProperty(DB_CONNECTION).asControllerService(DBCPService.class);
        this.loadTable();
    }
}public String getMonitor(String realValue) throws ProcessException {
    reloadOnce();
    return this.mapMonitor.get(realValue);
}Not sure the performance is good in this way. I also found the enable/disable 
is not override-able as declared with "final". As a service, I really want to 
control its life-cycle behavior, at least doing something at 
initialize/enable/disable/destroy stage. But the current controller service is 
lack of such capability, or I miss anything? Thanks and Regards.Kui


------------------ ???????? ------------------
??????: "oppo";<[email protected]>;
????????: 2017??12??22??(??????) ????9:42
??????: "users"<[email protected]>;

????: ?????? How to get controller service in Script Executor



Thanks, Matt. It works perfect!


Another question, I try to put the logic into a new controller service, as I 
want to load data from database and cache in service. So I write java code with 
api and bundle project and build in mvn, similar with NiFi standard service. In 
this enrichment service, I need DBCP service as a property, and I get 
connection and then load some data from database, cache them and prepare some 
"getXXX(String key)" method for Script in ExecutorScript processor. 


The following is the sample code for property definition and init method. I met 
the problem in init() when trying to get DBCP service and load table. Whatever 
I use "DBCPService.class" or "ControllerService.class", 
getControllerServiceIdentifiers() method throw exception. I assume "init()" is 
triggered when I configure enrichment service in one Processor Group and enable 
it, as I also configured DBCP service in enrichment service's property and also 
configure DBCP properties like JDBC URL, user/pwd, etc., I hope I can get the 
connection, but fail. 


See the exception from the log. It sounds Controller Service has only one 
instance in NiFi JVM, and init() is triggered at startup so it doesn't know 
processor group information? 


------------------------------- Code ------------------------------
    public static final PropertyDescriptor DB_CONNECTION =
            new PropertyDescriptor.Builder()
                    .name("data.source")
                    .displayName("Data Source")
                    .description("Database connection information")
                    .required(true)
                    .identifiesControllerService(DBCPService.class)
                    .expressionLanguageSupported(false)
                    .build();
                    
    @Override
    protected void init(final ControllerServiceInitializationContext context) 
throws InitializationException {
        final List<PropertyDescriptor> properties = new ArrayList<>();
        properties.add(DB_CONNECTION);
        this.properties = Collections.unmodifiableList(properties);
        Set<String> identifiers = 
context.getControllerServiceLookup().getControllerServiceIdentifiers(DBCPService.class);
        // doesn't work if I use "identifiers.toArray()[0]" because identifiers 
is of 0 length
        this.dbcpService = 
(DBCPService)context.getControllerServiceLookup().getControllerService("785b224c-0160-1000-d75e-dcb255cedd6a");
        this.loadTable();
    }



------------------------------- log ------------------------------
    Caused by: 
org.apache.nifi.controller.exception.ControllerServiceInstantiationException: 
java.lang.UnsupportedOperationException: Cannot obtain Controller Service 
Identifiers for service type interface 
org.apache.nifi.controller.ControllerService without providing a Process Group 
Identifier
        at 
org.apache.nifi.controller.service.StandardControllerServiceProvider.createControllerService(StandardControllerServiceProvider.java:174)
        at 
org.apache.nifi.controller.FlowController.createControllerService(FlowController.java:3313)
        at 
org.apache.nifi.controller.service.ControllerServiceLoader.createControllerService(ControllerServiceLoader.java:198)
        at 
org.apache.nifi.controller.service.ControllerServiceLoader.loadControllerServices(ControllerServiceLoader.java:114)
        at 
org.apache.nifi.controller.StandardFlowSynchronizer.addProcessGroup(StandardFlowSynchronizer.java:1078)
        at 
org.apache.nifi.controller.StandardFlowSynchronizer.sync(StandardFlowSynchronizer.java:322)
        ... 33 common frames omitted
Caused by: java.lang.UnsupportedOperationException: Cannot obtain Controller 
Service Identifiers for service type interface 
org.apache.nifi.controller.ControllerService without providing a Process Group 
Identifier
        at 
org.apache.nifi.controller.service.StandardControllerServiceProvider.getControllerServiceIdentifiers(StandardControllerServiceProvider.java:678)
        at 
org.apache.nifi.controller.service.StandardControllerServiceInitializationContext.getControllerServiceIdentifiers(StandardControllerServiceInitializationContext.java:60)
        at 
org.apache.nifi.dbproxy.EnrichmentServiceImpl.init(EnrichmentServiceImpl.java:76)
        at 
org.apache.nifi.controller.AbstractControllerService.initialize(AbstractControllerService.java:43)
        at 
org.apache.nifi.controller.service.StandardControllerServiceProvider.createControllerService(StandardControllerServiceProvider.java:148)
        ... 38 common frames omitted



------------------ ???????? ------------------
??????: "Matt Burgess";<[email protected]>;
????????: 2017??12??22??(??????) ????3:19
??????: "users"<[email protected]>;

????: Re: How to get controller service in Script Executor



Kui,

The getControllerService() method requires a controller service (CS)
identifier, not the name (because names are not necessarily unique).
To get the CS by name, you have to get the list of all CSs and match
on the name. I have an example in Groovy on my blog [1], but here is a
similar one ported to Nashorn/Javascript:

var ControllerService =
Java.type('org.apache.nifi.controller.ControllerService');
var lookup = context.getControllerServiceLookup();
var dbServiceName = databaseConnectionPoolName.getValue();
var dbcpServiceId = null;
for each(var cs in
lookup.getControllerServiceIdentifiers(ControllerService.class)) {
  if(lookup.getControllerServiceName(cs) == dbServiceName) {
    dbcpServiceId = cs;
    break;
  }
}
if(dbcpServiceId != null) {
  var conn = lookup.getControllerService(dbcpServiceId).getConnection();
  var stmt = conn.createStatement();
  var resultSet = stmt.execute("SELECT * FROM users");
  // do stuff here
  log.info('***** do stuff here *****');
} else {
  log.error("Couldn't find Controller Service: " + dbServiceName);
}


Regards,
Matt

[1] http://funnifi.blogspot.com/2016/04/sql-in-nifi-with-executescript.html

On Thu, Dec 21, 2017 at 4:09 AM, oppo <[email protected]> wrote:
> Hi,
>
> I try to use javascript in processor to do some enrichment and
> transformation, but i don't know how to concat the service identifier.
> As it is ExecutorScript processor, I can't add a service reference in
> properties, I can only add service in current Group (I put logic in one
> Process Group), I have name and id of service, I enable the service.
>
> See the following code:
>
> var enrichmentService =
> context.getControllerServiceLookup().getControllerService("DBCPConnectionPool");
> if(enrichmentService == null) {
>     log.warn("Can't find DBCPConnectionPool");
>     session.transfer(flowFile, REL_FAILURE);
> }
>
> It always jump to null condition.
>
> Anyone can help?
>
> Kui

Reply via email to