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