I'm trying to create an actor in Java Play Framework (2.5.10) for running periodic tasks. When my application runs, however, I get the error No implementation for akka.actor.ActorRef was bound (detailed error messages provided later in this post). I'm sure the mistake is pretty basic, but I'm new to the whole actors thing and am having trouble figuring it out.
Here's the class (root-level Module.java) that binds the scheduler class and the actor:
public class Module extends AbstractModule implements AkkaGuiceSupport {
#Override
public void configure() {
// Use the system clock as the default implementation of Clock
bind(Clock.class).toInstance(Clock.systemDefaultZone());
// Ask Guice to create an instance of ApplicationTimer when the
// application starts.
bind(ApplicationTimer.class).asEagerSingleton();
// Set AtomicCounter as the implementation for Counter.
bind(Counter.class).to(AtomicCounter.class);
// bind the ECWID data importer
bind(ImportScheduler.class).asEagerSingleton();
bindActor(UserImportActor.class, UserImportActor.ACTOR_NAME);
}
}
The scheduler class:
#Singleton
public class ImportScheduler {
#Inject
public ImportScheduler(final ActorSystem actorSystem, final ActorRef UserImportActor) {
actorSystem.scheduler().schedule(
Duration.create(1, TimeUnit.SECONDS),
Duration.create(1, TimeUnit.SECONDS),
UserImportActor,
0,
actorSystem.dispatcher(),
UserImportActor
);
}
}
And finally, the actor class:
public class UserImportActor extends UntypedActor {
public static final String ACTOR_NAME = "user_import_actor";
#Override
public void onReceive(Object message){
Logger.info("The user import actor was called!");
}
}
When the application runs, here's the error that I see (the full error is too long - I think the first few lines will suffice):
! #72bagdfd4 - Internal server error, for (GET) [/] ->
play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:
1) No implementation for akka.actor.ActorRef was bound.
while locating akka.actor.ActorRef
for parameter 1 at services.ecwid.db.ImportScheduler.<init>(ImportScheduler.java:12)
at Module.configure(Module.java:34) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
Any idea what I'm missing?
The bindActor method annotates your ActorRef with a name - the name of the actorRef itself.
Could you try using the #Named annotation?
#Inject
public ImportScheduler(final ActorSystem actorSystem, #Named("user_import_actor") ActorRef UserImportActor) {
...
}
Related
I'm trying to receive message through Grpc service, send it to Kafka Emitter, and return some value back.
#Singleton
#GrpcService
public class MessageService implements protobuf.MessageService{
#Inject
#Channel("hello-out")
Emitter<Record<String, GeneratedMessageV3>> emitter;
#Override
public Uni<EnvelopeReply> processMessage(Envelope request) {
return Uni.createFrom().completionStage(
emitter.send(Record.of(request.getKey(), request))
).replaceWith(EnvelopeReply.newBuilder().build());
}
}
During build, I'm getting next error:
Error injecting org.eclipse.microprofile.reactive.messaging.Emitter<io.smallrye.reactive.messaging.kafka.Record<java.lang.String, com.google.protobuf.GeneratedMessageV3>> com.test.MessageService.emitter
...
Caused by: javax.enterprise.inject.spi.DefinitionException: SRMSG00019: Unable to connect an emitter with the channel `hello-out`
It works properly with Rest resource.
Without going deeply into the topic, here's my solution:
You can't inject Kafka Emmiter directly to grpc service, it'll throw an exception.
GrpcService <- Emitter<Record...>
Possible reason(I'm sure Quarkus team will reply lower with correct solution :)) is that all GrpcServices are of #Singleton type, and they can't have lazy-initialised properties, they need to have something directly injected. Emitter is generated at a later stage.
By adding a wrapper class you're solving all the headaches, so:
GrpcService <- KafkaService <- Emitter<Record...>
#ApplicationScoped
public class KafkaService {
#Inject
#Channel("hello-out")
Emitter<Record<String, GeneratedMessageV3>> emitter;
// Implement this part properly, added just for example
public Emitter<Record<String, GeneratedMessageV3>> getEmitter() {
return emitter;
}
}
...
#Singleton
#GrpcService
public class MessageService implements protobuf.MessageService {
#Inject
KafkaService kafkaService;
#Override
public Uni<EnvelopeReply> processMessage(Envelope request) {
// use metadata if needed
Map<String, String> metadataMap = request.getMetadataMap();
return Uni.createFrom().completionStage(
kafkaService.getEmitter().send(Record.of(request.getKey(), request))
).replaceWith(EnvelopeReply.newBuilder().build());
}
}
Before Akka 2.6, or with classic Actors, one can write an Akka Extension to have access to Spring's #Inject annotation in Akka untyped actors.
One example of this is: https://github.com/typesafehub/activator-akka-java-spring/blob/master/src/main/java/sample/SpringExtension.java
However, this does not work for the new Akka Typed actors.
Akka's documentation does not show how to make such an extension (but it does show how to make simple extensions: https://doc.akka.io/docs/akka/current/typed/extending.html#building-an-extension).
So far, I wrote this beginning of extension, but I don't know how to link Spring's ApplicationContext with the actor system:
import org.springframework.context.ApplicationContext;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Extension;
import akka.actor.typed.ExtensionId;
public class SpringExtension implements Extension {
private volatile ApplicationContext applicationContext;
private SpringExtension(final ActorSystem<?> system) {
// TODO: What do you put here?
}
void initialize(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public static class Id extends ExtensionId<SpringExtension> {
private static final Id instance = new Id();
private Id() {}
// called once per ActorSystem
#Override
public SpringExtension createExtension(final ActorSystem<?> system) {
return new SpringExtension(system);
}
public static SpringExtension get(final ActorSystem<?> system) {
return instance.apply(system);
}
}
}
How do you write an Akka Extension for Typed Actors allowing to use Spring DI in typed actors?
May be this is not exactly what you need, but I think found a way how to inject typed actors without using an extension.
We can create Behavior as a bean, inject all needed dependencies and pass it to another actor, where spawn the actor based on defined Behavior.
Let's assume we have PrintActor that can print messages using PrintService, and GreetActor that uses GreetService and spawns PrintActor. We can define beans like this:
#Bean
public Behavior<String> printActorBehavior(PrintService printService) {
return Behaviors.setup(ctx -> new PrintActor(ctx, printService));
}
#Bean
public Behavior<GreetActor.Greet> greetActorBehavior(GreetService greetService,
Behavior<String> printerActorBehavior) {
return Behaviors.setup(ctx -> new GreetActor(ctx, greetService, printerActorBehavior));
}
And then, in GreetActor we just create actor from injected Behavior by calling getContext().spawn(printerActorBehavior, "printer");
public class GreetActor extends AbstractBehavior<GreetActor.Greet> {
private GreetService greetService;
private Behavior<String> printerActorBehavior;
public GreetActor(ActorContext<Greet> context,
GreetService greetService,
Behavior<String> printerActorBehavior) {
super(context);
this.greetService = greetService;
this.printerActorBehavior = printerActorBehavior;
}
#Override
public Receive<Greet> createReceive() {
return newReceiveBuilder()
.onMessage(Greet.class, this::onGreet)
.build();
}
private Behavior<Greet> onGreet(Greet msg) {
ActorRef<String> printer = getContext().spawn(printerActorBehavior, "printer");
printer.tell(greetService.greet(msg.name));
return this;
}
#Value
static class Greet {
String name;
}
}
Since we can not create actors from outside of actor system in Akka Typed, I think there is no proper way to inject ActorRef with Spring.
We can try to inject Spring context into actor, spawn some actors and put them into Spring context, but I think this is not a good way to work with both frameworks.
I'm trying to migrate from Play 2.3 to 2.5 but I have an issue on replacing the GlobalSettings.OnStart method.
In my old Global class that was extending GlobalSettings, in the onStartMethod I was initialising the Global Config and reading basic things from the DB.
I have created a new class and I m moving the code from the onStart method to the constructor of this one as mentioned in the doc.
https://www.playframework.com/documentation/2.5.x/GlobalSettings
https://www.playframework.com/documentation/2.4.x/PluginsToModules
I'm doing the binding as eagerSingleton from an AbstractModule. The class is correctly started but I keep getting the error:
GlobalBootstrapModule.configure:20 - Binding
OnStartConfig.:35 - Global...on start
Caused by: java.lang.RuntimeException: No EntityManager bound to this thread. Try wrapping this call in JPAApi.withTransaction, or ensure that the HTTP context is setup on this thread.
This is my code:
New class to replace onStart
public class OnStartConfig implements StartConfigInterface{
private final JPAApi jpaApi;
#Inject
public OnStartConfig(Application application, JPAApi jpa){
this.jpaApi = jpa;
Logger.debug("Global...on start");
jpaApi.withTransaction( ()-> {
GlobalConfiguration.aggregationCriteria = AggregationCriterion.getAll();
});
}
The interface I'm extending is just an empty placeholder.
AbstractModule used for the binding:
public class GlobalBootstrapModule extends AbstractModule {
#Override
protected void configure() {
Logger.debug("Binding");
bind(StartConfigInterface.class).to(OnStartConfig.class).asEagerSingleton();
}
}
And I have enabled the module in the application.conf file:
play {
modules {
enabled += modules.GlobalBootstrapModule
}
}
I suppose the problem is due to the lack of HttpContext. Where can I grab it from during the initialisation?
Any help would be hugely appreciated
I'm currently trying to implement injection in a Java console application using Guice. The application imports XML files in a database. Every import operation is done in an AbstractImporter, which can either be a UserImporter, a ScheduleImporter, etc.
public class ScheduleMigrator extends AbstractMigrator {
private final UserImporter userImporter;
private final ScheduleImporterFactory scheduleImporterFactory;
#Inject
public ScheduleMigrator(UserImporter userImporter,
ScheduleImporterFactory scheduleImporterFactory) {
this.userImporter = userImporter;
this.scheduleImporterFactory = scheduleImporterFactory;
}
public void migrate() {
// Migrate users
userImporter.run();
// Migrate schedules for each type
for (ScheduleType scheduleTypes : ScheduleType.values()) {
ScheduleImporter importer =
scheduleImporterFactory.create(scheduleTypes);
importer.run();
}
}
}
public class UserImporter extends AbstractImporter {
private final UserTransformer userTransformer;
private final ConfigurationService configurationService;
#Inject
public UserImporter(UserTransformer userTransformer,
ConfigurationService configurationService) {
this.userTransformer = userTransformer;
this.configurationService = configurationService;
}
public void run() {
// do stuff here
}
}
#Singleton
public class UserTransformer {
// ...code ommited...
}
#ImporterScoped
public class ConfigurationService {
// ...code ommited...
}
I have successfully created my own scope (#ImporterScoped) for classes that should only be available and instantiated only in an Importer. The scope was created by following the steps in the wiki. My problem is, how should I enter and exit the scope in ScheduleMigrator?
As you can see in ScheduleMigrator, each Importer is injected and its run() method is invoked. There are also factories (based on Guice's #AssistedInject feature). This is where I want each scope to start and end, UserImporter and ScheduleImporterFactory should run in their own scope.
This is a rough idea of what I'm trying to achieve:
importerScope.enter();
(new UserImporter()).run();
importerScope.exit();
Guice's documentation mentions the use of interceptors, but I'm a little lost on how it can be implemented.
Using AOP seems a very over-engineered approach and might introduce problems. When do I enter the scope? When do I exit? What happens if I instantiate two Importer objects?
Instead, I added a runScoped method in AbstractMigrator that takes a Runnable and executes it. Using injection I get the ImporterScope scope, enter and exit it appropriately.
protected void runScoped(Runnable function)
{
scenarioScope.enter();
try {
function.run();
}
finally {
scenarioScope.exit();
}
}
Usage:
runScoped(() -> {
ScheduleImporter importer =
scheduleImporterFactory.create(scheduleTypes);
importer.run();
});
This introduces one problem though. In ScheduleMigrator, I can't have Importers injected, because their instantiation would occur outside of a scope and Guice throws an OutOfScopeException. I had to wrap each Importer in a Provider.
private final Provider<UserImporter> userImporterProvider;
runScoped(() -> {
UserImporter importer = userImporterProvider.get();
importer.run();
});
Given a consumer which uses a service, how can this consumer select a specific provider dynamically using declarative service ?
Example
Service.java
public interface Service {
public void do();
}
Provider1.java
public class Provider1 implements Service {
#Override
public void do(){
//a way
}
}
Provider2.java
public class Provider2 implements Service {
#Override
public void do(){
//another way
}
}
Consumer.java
public class Consumer {
private Service myService;
protected void bindService(Service s){ // Actually it's Provider1
myService = s;
}
protected void unbindService(Service s){
myService = null;
}
public void useThisKindOfService(String s){
// Do something crazy
}
}
So, what I would like it's instead of "Do something crazy", to find a way to reconfigure the consumer in order to release Provider1 and ask for Provider2.
Is it possible ?
Update related to "Duplicate Question"
OSGI/Felix Declarative services: How to filter the services to be bound
In my context I cannot use the declarative target because the value of the target has to be know at build time, in my case the target could be defined by a user at runtime.
Components of Declarative Services can be configured via ConfigurationAdmin. By doing that, the configuration of the component can be changed at runtime.
You can also change the configuration of myService.target via ConfigurationAdmin at runtime. If you do that, another reference will be bound to your component.
If the policy of the reference of your component is dynamic, the new reference will be bound without reactivating your component.
For more information, see the Declarative Services chapter of the OSGi Compendium specification.