I have a Play 2.4 application. Trying to kick off a weekly task when application starts. The current recommendation is to do that in a constructor for an eagerly injected class (Guice). However, my task needs access to a service. How can I inject that service into my task without getting an error:
Error injecting constructor, java.lang.RuntimeException: There is no started application
?
You need to use constructor injection in your ApplicationStart class and provide an ApplicationModule to bind it eagerly.
In your application.conf:
play.modules.enabled += "yourPath.AppModule"
In your AppModule Class:
public class AppModule extends AbstractModule {
#Override
protected void configure() {
Logger.info("Binding application start");
bind(ApplicationStart.class).asEagerSingleton();
Logger.info("Binding application stop");
bind(ApplicationStop.class).asEagerSingleton();
}
}
In your ApplicationStart class:
#Singleton
public class ApplicationStart {
#Inject
public ApplicationStart(Environment environment, YourInjectedService yourInjectedService) {
Logger.info("Application has started");
if (environment.isTest()) {
// your code
}
else if(
// your code
}
// you can use yourInjectedService here
}
}
In case you need it; ApplicationStop:
#Singleton
public class ApplicationStop {
#Inject
public ApplicationStop(ApplicationLifecycle lifecycle) {
lifecycle.addStopHook(() -> {
Logger.info("Application shutdown...");
return F.Promise.pure(null);
});
}
}
Related
I am using dependency injection according to google sample
The only external dependency I can pass is through AppComponent builder
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,
CoreActivityModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
and injected in app like this
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.application(myApplication)
.build().inject(myApplication);
...
}
According to document injecting in Activity looks like this. I added what I would like to achieve.
public class YourActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
AndroidInjection
//.builder() THIS IS WHAT I WANT TO ACHIEVE
//.addActivityContext(this) THIS IS WHAT I WANT TO ACHIEVE
//.build() THIS IS WHAT I WANT TO ACHIEVE
.inject(this);
super.onCreate(savedInstanceState);
}
}
but the question is how can I add additional parameter to subComponent.
#Subcomponent
public interface CoreActivitySubComponent extends AndroidInjector<CoreAppActivity> {
// #Subcomponent.Builder
// interface Builder {
// Builder addContext(Context context) //did not work
// CoreActivitySubComponent build(); //did not work
// }
//==or using abstract class
// in this option I do not know where to add parameter to this builder
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
}
}
Did you add the ContextModule to your #Subcomponent similar to this answer?
I think your #Subcomponent should look something like this:
#Subcomponent(module = {ContextModule.class})
interface MainTabActivityComponent extends AndroidInjector<CoreAppActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
}
And finally don't forget to include this #Subcomponent in your binding-module.
One last question: is this really required?
I found that using the AndroidInjector on the Application as well as Activities and Fragments will give me the correct corresponding Context when I inject it.
The problem was that Dagger 2.1.0 method AndroidInjection.inject(this); which is supposed to be used in Activity and Fragment, do not provide any builder to add external dependency.
I wanted to create general module which depends on Activity/Fragment context.
sample:
public class ToastController {
private Context context;
#Inject
public ToastController(Context context) {
this.context = context;
}
public void showToast(#StringRes int res) {
Toast.makeText(context, context.getText(res), Toast.LENGTH_SHORT).show();;
}
}
But I was not able to generalize it to the level, that I could provide just one context modude, instead I had to do create binds module for every single Activity/ Fragment that uses this module.
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,// IMPORTANT CLASS
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
This is a place, where I provide context module for each Activity
#Module
public abstract class MainTabActivityModule
#ContributesAndroidInjector(modules = ContextMainTabActivityModule.class)//THIS MODULE
abstract MainTabActivity contributeMainActivity();
}
and Context is provided using #Binds annotation
#Module
public abstract class ContextMainTabActivityModule {
#Binds
abstract Context provideContext(MainTabActivity featureActivity);
}
=====================
It can be done by overriding method seedInstance according to sample
I tried this, but it did not work for me
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
next class
#Module
public class ContextModule {
private CoreAppActivity coreAppActivity;
#Provides
Context getContext() {
return coreAppActivity.getBaseContext();
}
public ContextModule(CoreAppActivity coreAppActivity) {
this.coreAppActivity = coreAppActivity;
}
}
I am exploring the new dagger.android from Dagger 2.11. I hope not to have to create custom scope annotation like #PerActivity. So far I was able to do the following:
1) Define Application scope Singletons and injecting them into activities.
2) Define Activity scope non-Singleton dependencies and injecting them into their activities using #ContributesAndroidInjector
What I cannot figure out is how to have an Application scope Singleton and Activity scope non-Singletons using it.
In the example below, I would like my Activity scope MyActivityDependencyA and MyActivityDependencyB to have access to a Singleton MyActivityService
The setup below results in:
Error:(24, 3) error: com.example.di.BuildersModule_BindMyActivity.MyActivitySubcomponent
(unscoped) may not reference scoped bindings:
#Singleton #Provides com.example.MyActivityService
com.example.MyActivitySingletonsModule.provideMyActivityService()
Here is my setup. Note, I defined separate MyActivitySingletonsModule and MyActivityModule since I could not mix Singleton and non-Singleton dependencies in the same Module file.
#Module
public abstract class BuildersModule {
#ContributesAndroidInjector(modules = {MyActivitySingletonsModule.class, MyActivityModule.class})
abstract MyActivity bindMyActivity();
}
}
and
#Module
public abstract class MyActivityModule {
#Provides
MyActivityDependencyA provideMyActivityDependencyA(MyActivityService myActivityService){
return new MyActivityDependencyA(myActivityService);
}
#Provides
MyActivityDependencyB provideMyActivityDependencyB(MyActivityService myActivityService) {
return new MyActivityDependencyB(myActivityService);
}
}
and
#Module
public abstract class MyActivitySingletonsModule {
#Singleton
#Provides
MyActivityService provideMyActivityService() {
return new MyActivityService();
}
}
and
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
BuildersModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(App application);
AppComponent build();
}
void inject(App app);
}
Is it even possible to do what I am trying to do without defining custom scope annotations?
Thanks in advance!
Why avoid custom scopes? Custom scopes are still required for the new dagger.android dependency injection framework introduced in Dagger 2.10+.
"My understanding is #ContributesAndroidInjector removes the need for custom annotation and I was able to prove it by using non-singletons defined in the activity scope without any issues."
#ContributesAndroidInjector (available in v2.11) does not remove the need for custom scopes. It merely replaces the need to declare #Subcomponent classes that does not make use of #Subcomponent.Builder to inject dependencies required by the component at runtime. Take a look at the below snippet from the official dagger.android user guide about #ContributesAndroidInjector;
"Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use #ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with #ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well."
#ActivityScope
#ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();
The key here is "If the subcomponent needs scopes, apply the scope annotations to the method as well."
Take a look at the following code for an overview of how to use #Singleton, #PerActivity, #PerFragment, and #PerChildFragment custom scopes with the new dagger.android injection framework.
// Could also extend DaggerApplication instead of implementing HasActivityInjector
// App.java
public class App extends Application implements HasActivityInjector {
#Inject
AppDependency appDependency;
#Inject
DispatchingAndroidInjector<Activity> activityInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create().inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityInjector;
}
}
// AppModule.java
#Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity mainActivityInjector();
}
// AppComponent.java
#Singleton
#Component(modules = AppModule.class)
interface AppComponent {
void inject(App app);
}
// Could also extend DaggerActivity instead of implementing HasFragmentInjector
// MainActivity.java
public final class MainActivity extends Activity implements HasFragmentInjector {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency;
#Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
addFragment(R.id.fragment_container, new MainFragment());
}
}
#Override
public final AndroidInjector<Fragment> fragmentInjector() {
return fragmentInjector;
}
}
// MainActivityModule.java
#Module
public abstract class MainActivityModule {
#PerFragment
#ContributesAndroidInjector(modules = MainFragmentModule.class)
abstract MainFragment mainFragmentInjector();
}
// Could also extend DaggerFragment instead of implementing HasFragmentInjector
// MainFragment.java
public final class MainFragment extends Fragment implements HasFragmentInjector {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency; // same object from MainActivity
#Inject
FragmentDependency fragmentDepency;
#Inject
DispatchingAndroidInjector<Fragment> childFragmentInjector;
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Perform injection here before M, L (API 22) and below because onAttach(Context)
// is not yet available at L.
AndroidInjection.inject(this);
}
super.onAttach(activity);
}
#Override
public void onAttach(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Perform injection here for M (API 23) due to deprecation of onAttach(Activity).
AndroidInjection.inject(this);
}
super.onAttach(context);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
addChildFragment(R.id.child_fragment_container, new MainChildFragment());
}
}
#Override
public final AndroidInjector<Fragment> fragmentInjector() {
return childFragmentInjector;
}
}
// MainFragmentModule.java
#Module
public abstract class MainFragmentModule {
#PerChildFragment
#ContributesAndroidInjector(modules = MainChildFragmentModule.class)
abstract MainChildFragment mainChildFragmentInjector();
}
// MainChildFragment.java
public final class MainChildFragment extends Fragment {
#Inject
AppDependency appDependency; // same object from App
#Inject
ActivityDependency activityDependency; // same object from MainActivity
#Inject
FragmentDependency fragmentDepency; // same object from MainFragment
#Inject
ChildFragmentDependency childFragmentDepency;
#Override
public void onAttach(Context context) {
AndroidInjection.inject(this);
super.onAttach(context);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_child_fragment, container, false);
}
}
// MainChildFragmentModule.java
#Module
public abstract class MainChildFragmentModule {
}
// PerActivity.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerActivity {
}
// PerFragment.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerFragment {
}
// PerChildFragment.java
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerChildFragment {
}
// AppDependency.java
#Singleton
public final class AppDependency {
#Inject
AppDependency() {
}
}
// ActivityDependency.java
#PerActivity
public final class ActivityDependency {
#Inject
ActivityDependency() {
}
}
// FragmentDependency.java
#PerFragment
public final class FragmentDependency {
#Inject
FragmentDependency() {
}
}
// ChildFragmentDependency.java
#PerChildFragment
public final class ChildFragmentDependency {
#Inject
ChildFragmentDependency() {
}
}
For a complete dagger.android 2.11 setup guide using #ContributesAndroidInjector and custom scopes mentioned above, read this article.
There are some problems here: firstly, ActivitySingleton doesn't make much sense. A dependency is either a singleton (per app, or app scoped) or not.
If it is not a singleton it could be activity scoped (per activity). This would mean it lived and died with the Activity i.e., that its lifecycle was congruent with that of the Activity itself and hence it would be destroyed with the onDestroy of the Activity.
That doesn't mean that everything that is injected inside an Activity must be #PerActivity. You can still inject #Singleton dependencies there (like per app OkHttpClient for instance). However, these #Singleton dependencies will not be bound in the module set that composes a #PerActivity component. Instead, they will be bound in the module set for parent components and obtained through the component hierarchy (dependent components or sub-components).
These means that your ActivitySingletonsModule is incorrect, see the comments in the code below:
#Module
public abstract class MyActivitySingletonsModule {
//#Singleton
//^^ remove the annotation here if you want to use the
//in your ActivityComponent
//If you need this as a per-app singleton, then include
//this module at the AppComponent level
#Provides
MyActivityService provideMyActivityService() {
return new MyActivityService();
}
}
I do not understand the reluctance to define a custom scope. These are extremely lightweight and can improve readability. Here is the single line of code you would need to create a #PerActivity scope.
#Scope #Retention(RetentionPolicy.RUNTIME) public #interface PerActivity {}
I suspect the concept of scopes is unclear and this is leading to the reluctance. Admittedly, it can be rather confusing. However there are some really good canonical answers that help clarify. I would suggest this question as a start:
Dagger2 Custom Scopes : How do custom-scopes (#ActivityScope) actually work?
Guice module integration issue with REST
I have define one AOP guice based module, but when I tried to integrate with REST code, methodInvocation.proceed retun null.
What might be best way to solve this issue.
Define AOP Guice based module as below
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface NotOnWeekends {}
public class WeekendBlocker implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Calendar today = new GregorianCalendar();
if (today.getDisplayName(DAY_OF_WEEK, LONG, ENGLISH).startsWith("S")) {
throw new IllegalStateException(
invocation.getMethod().getName() + " not allowed on weekends!");
}
return invocation.proceed();
}
}
public class NotOnWeekendsModule extends AbstractModule {
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class),
new WeekendBlocker());
}
}
But I tried to Integrate this with my REST API
public class WorkerBean implements Worker {
#Autowired
private WorkerRepository workerRepository;
#Override
#NotOnWeekends
public Collection<Worker> findAll() {
Collection<Worker> workers = workerRepository.findAll();
return workers;
}
#RestController
public class WorkerController {
#RequestMapping(
value = "/api/workers",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Collection<Worker>> getWorkers() {
Worker worker = Guice.createInjector(new NotOnWeekendsModule()).getInstance(Worker.class);
Collection<Worker> worker = worker.findAll(); // return null
....
}
I want to setup my actors inside of a play app, for example, I have an actor that will poll a message queue or run every x minutes.
I renamed the actor system in my play app, so I now how to get the actor system.
play.akka.actor-system = "myAkka"
I know I can get the actor system using dependency injection inside of a controller but I don't need it at the controller level, I need to do this when my application starts outside of the request/response level.
One of the the way is to crate your actors in an eager singleton.
Create singleton like this:
package com.app;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import javax.inject.Inject;
import javax.inject.Singleton;
#Singleton
public class ActorBootstrap {
private ActorRef somaActor;
#Inject
public ActorBootstrap(ActorSystem system) {
// Craete actors here: somaActor = system.actorOf()
}
public ActorRef getSomaActor() {
return somaActor;
}
}
Define the singleton as eager in guice module as follows:
package com.app;
import com.google.inject.AbstractModule;
public class AppModule extends AbstractModule {
#Override
protected void configure() {
bind(ActorBootstrap.class).asEagerSingleton();
}
}
See https://www.playframework.com/documentation/2.5.x/JavaDependencyInjection#Eager-bindings for details.
Register your module with Play (add the following line to application.conf):
play.modules.enabled += "com.app.AppModule"
See https://www.playframework.com/documentation/2.5.x/JavaDependencyInjection#Programmatic-bindings for details.
Here is a basic example of a scheduled actor implementation.
The actor schedules some periodic work:
public class ScheduledActor extends AbstractActor {
// Protocol
private static final String CANCEL = "cancel";
private static final String TICK = "tick";
// The first polling in 30 sec after the start
private static final int TICK_INTERVAL_SEC = 90;
private static final int TICK_INTERVAL_SEC = 90;
private Cancellable scheduler;
public static Props props() {
return Props.create(ScheduledActor.class, ()->new ScheduledActor());
}
public ScheduledActor() {
receive(ReceiveBuilder
.matchEquals(TICK, m -> onTick())
.matchEquals(CANCEL, this::cancelTick)
.matchAny(this::unhandled)
.build());
}
#Override
public void preStart() throws Exception {
getContext().system().scheduler().scheduleOnce(
Duration.create(ON_START_POLL_INTERVAL, TimeUnit.SECONDS),
self(),
TICK,
getContext().dispatcher(),
null);
}
#Override
public void postRestart(Throwable reason) throws Exception {
// No call to preStart
}
private void onTick() {
// do here the periodic stuff
...
getContext().system().scheduler().scheduleOnce(
Duration.create(TICK_INTERVAL_SEC, TimeUnit.SECONDS),
self(),
TICK,
getContext().dispatcher(),
null);
}
public void cancelTick(String string) {
if (scheduler != null) {
scheduler.cancel();
}
}
}
The actor life-cycle handler creates and actor and cancels it upon application stop:
public class ScheduledActorMonitor {
private ActorRef scheduler;
private ActorSystem system;
#Inject
public ScheduledActorMonitor(ActorSystem system, ApplicationLifecycle lifeCycle) {
this.system = system;
initStopHook(lifeCycle);
}
public void startPolling() {
scheduler = system.actorOf(ScheduledActor.props();
}
public void cancelTick() {
if (scheduler != null) {
scheduler.tell(HelloScheduler.CANCEL, null);
}
}
private void initStopHook(ApplicationLifecycle lifeCycle) {
lifeCycle.addStopHook(() -> {
cancelTick();
return CompletableFuture.completedFuture(null);
});
}
}
The StartupHandler is injected as a singleton; it receives in the constructor an actor monitor and starts polling:
#Singleton
public class StartupHandler {
#Inject
public StartupHandler(final ScheduledActorMonitor schedularMonitor) {
schedularMonitor.startPolling();
}
}
The StartupHandler is registered for injection by the default module Play Module:
public class Module extends AbstractModule {
#Override
public void configure() {
bind(StartupHandler.class).asEagerSingleton();
}
}
You can read more here.
I have a problem with #IocProvider (), annotation does not work.
The code is very similar to https://docs.jboss.org/author/display/ERRAI/Container+Wiring
public interface Test {
String getGreeting();
}
#ApplicationScoped
public class TestImpl implements Test {
public String getGreeting() {
return "Hello:)";
}
}
#IOCProvider
#Singleton
public class TestProvider implements Provider<Test> {
#Override
public Test get() {
return new TestImpl();
}
}
Then I want use DI in my broadcast service (errai-bus).
#Service
public class BroadcastService implements MessageCallback {
#Inject
Test test;
#Inject
MessageBus bus;
#Inject
public BroadcastService(MessageBus bus) {
this.bus = bus;
}
public void callback(Message message) {
MessageBuilder.createMessage()
.toSubject("BroadcastReceiver")
.with("BroadcastText", test.getGreeting()).errorsHandledBy(new ErrorCallback() {
#Override
public boolean error(Message message, Throwable throwable) {
return true;
}
}).sendNowWith(bus);
}
}
I get a error:
1) No implementation for com.gwtplatform.samples.basic.server.Test was bound.
while locating com.gwtplatform.samples.basic.server.Test
for field at com.gwtplatform.samples.basic.server.BroadcastService.test(BroadcastService.java:32)
at org.jboss.errai.bus.server.service.ServiceProcessor$1.configure(ServiceProcessor.java:118)
If I change the code to
#Inject
TestImpl test;
It works, but I need the provider. Do you have some idea?
Because you're trying to use #IOCProvider in server-side code. Errai IOC is completely client-side.