How to cleanup/teardown provided objects inside a Module - java

I have a custom Dagger Scope.
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface FeatureScope {
}
I have a Component/Module that provides a Presenter (any UI element will really work here) that is tied to this scope.
#FeatureScope
#Component(modules = {
CustomScopedModule.class
})
public interface CustomScopedComponent {
}
#Module
public class CustomScopedModule {
#FeatureScope
#Provides
Presenter providePresenter() {
return new Presenter();
}
}
I know that it is up to me to create/destroy this custom scope when it is no longer needed. So in my application I have:
public class MyApplication extends Application {
private CustomScopedComponent customScopedComponent;
public CustomScopedComponent getCustomScopedComponent() {
if (customScopedComponent == null) {
customScopedComponent = DaggerCustomScopedComponent.builder()
.contactsModule(new CustomScopedModule())
.build();
}
return customScopedComponent;
}
public void finishedWithCustomScopedComponent() {
customScopedComponent = null;
}
However I now need to "cleanup" after taking Component (and Presenter) out of scope. I can't just set customScopedComponent to null. I also need to call cleanup() on my Presenter:
class Presenter {
public void cleanup() {
// Dispose of all the things
// Release any DB connections
}
...
}
What is the best way to handle/create a call path to handle this? I see that Dagger doesn't really have any tools for this and that I need to roll my own solution Discussion
So I thought at least I could add a method to the module:
#Module
public class CustomScopedModule {
#FeatureScope
#Provides
Presenter providePresenter() {
return new Presenter();
}
public void cleanup() {
// call Presenter.cleanup();
}
}
And call it when destroying the component:
public class MyApplication extends Application {
private CustomScopedComponent customScopedComponent;
private CustomScopedModule customScopedModule;
...
public void finishedWithCustomScopedComponent() {
customScopedModule.cleanup();
customScopedComponent = null;
}
}
But my question is, how can the module's cleanup() method get access to the presenter? Only thing I can think of is just saving the presenter as a field inside the module:
#Module
public class CustomScopedModule {
private Presenter presenter;
#FeatureScope
#Provides
Presenter providePresenter() {
presenter = new Presenter(); // save instance to cleanup later
return presenter;
}
public void cleanup() {
if (presenter != null) {
presenter.cleanup();
}
}
}
This seems ugly and very un-Dagger to me.

Related

How not to use CompletableFuture as a shared state with EventListener in Spring

I need to asynchronously react to the #EventListener, therefore I've created something like this.
#Service
public class AsyncHandler {
private CompletableFuture<My> future;
#Async
public CompletableFuture<My> getMy() {
future = new CompletableFuture<>();
return future;
}
#EventListener
public void processEvent(MyEvent event) {
future.complete(event.my());
}
}
The problem here is that AsyncHandler is now stateful. Which is wrong.
And I don't want to use database, so is there any other way to make the bean stateless while using #EventListener?
You are right, your singleton has state, which is "not good".
One (possible) solution:
make the/refactor "statfeul part" to a "prototype" (request, session) scope bean.
make your "singleton" abstract!
inject the "stateful part" via "method injection" (we cannot "auto wire" lower(shorter-living) scope beans to higher ones...)
As code (example):
State holder:
public class MyStateHolder {
// State:
private CompletableFuture<My> future;
#Async // ?
public CompletableFuture<My> getMy() {
future = new CompletableFuture<>();
return future;
}
}
Abstract, (no #Service ..yet, no state!):
public abstract class AsyncHandler {
#EventListener
public void processEvent(MyEvent event) {
// !!
delegate().getMy().complete(event.my());
}
// and now only (abstract!):
public abstract MyStateHolder delegate();
}
Wiring :
#Configuration
class MyConfig {
#Bean
#Scope("prototype") // !
public MyStateHolder stateful() {
return new MyStateHolder();
}
#Bean // singleton/service:
public AsyncHandler asyncHandler() {
return new AsyncHandler() { // !
#Override // !
public MyStateHolder delegate() {
return stateful();// !;)
}
};
}
}
refs: (most of) https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
Especially:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-sing-prot-interaction

How to get a singleton injection in a class with Dagger in android

There is the DataAccessInterface class that is in charge of managing the database:
public class DataAccessInterface {
private DaoSession daoSession;
public DataAccessInterface () {
}
...
public void saveCar (Car car) {
daoSession.getCarDao (). insert (car);
}
}
DataAccessInterface injection is used in several Fragments with success. Example:
public class LoginFragment extends BaseFragment {
#Inject
DataAccessInterface dataAccessInterface;
...
public boolean initDatabase () throws SyncDataBaseException {
try {
dataAccessInterface.openSession (currentUser.getUsername ());
} catch (Exception e) {
throw new SyncDataBaseException ();
}
return true;
}
...
}
There is a BackendImp class (No Fragment or Activity) that in the background queries a rest service and saves the response in the database. The injection does not work, it is always null:
public class BackendImp {
#Inject
DataAccessInterface dataAccessInterface;
public void save () {
Car car = unloadCar ()
dataAccessInterface.saveCar (car);
}
The AbstractActivityComponent looks like this
#PerActivity
#Component (dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface AbstractActivityComponent {
Activity activity ();
final class Initializer {
public static AbstractActivityComponent init (Activity activity) {
return DaggerAbstractActivityComponent.builder ()
.applicationComponent (DaggerManager.getInstance (). appComponent ())
.activityModule (new ActivityModule (activity))
.build ();
}
}
void inject (LoginFragment inject);
void inject (BackendImp inject);
}
ApplicationModule:
#Module
public class ApplicationModule {
private final Application application;
private final User currentUser;
public ApplicationModule (Application application) {
this.application = application;
this.currentUser = getUser ();
}
#Provides
#Singleton
DataAccessInterface dataAccessInterface () {
return new DataAccessInterface (userProfile ());
}
}
And ApplicationComponent
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(Application application);
final class Initializer {
public static ApplicationComponent init(Application app) {
return DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(app))
.build();
}
}
Application application();
Context context();
DataAccessInterface dataAccessInterface();
}
Error:
W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.service.DataAccessInterface.saveCar(Car)' on a null object reference
Edit:
Based on Nitrodon's question in the comments:
The BackendImp functions are called from a Worker, since they will be done every hour. I wanted a single instance so I did the following which is probably wrong:
public class MainApp extends Application {
public static BackendService backendService;
#Override
public void onCreate () {
super.onCreate ();
backendService = new BackendImp ();
}
public static void callWorker () {
...
workManager.enqueue (updateWorkRequest);
}
And the Worker:
public class updateWorker extends Worker {
...
#Override
public Result doWork () {
Result result = Result.retry ();
try {
backend = MainApp.backendService;
backend.save ();
result = Result.success ();
} catch (Exception e) {
result = Result.retry ();
}
return result;
}
Dagger doesn't hook into constructor calls. You have to tell Dagger to inject dependencies into BackendImp.
You have an inject(BackendImp inject) method in your activity component. This would work, but it's in the wrong place, so your application class can't access it. Putting this method in the application component would work:
#Override
public void onCreate () {
super.onCreate ();
backendService = new BackendImp();
// I assume you created the application component somewhere in here.
component.inject(backendService);
}
However, members injection methods are generally discouraged where they can be avoided. There's no other choice in Activity subclasses, since they are instantiated by the framework, but something like BackendImp is entirely under your control, so you can and should let Dagger create it for you.
To do this, place BackendImp itself in the application component by giving it the #Singleton scope and an #Inject constructor:
#Singleton
public class BackendImp {
final DataAccessInterface dataAccessInterface;
#Inject
BackendImp(DataAccessInterface dataAccessInterface) {
this.dataAccessInterface = dataAccessInterface;
}
// ...
}
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
// ...
// Use this in your application instead of new BackendImp()
BackendImp backendImp();
// Many tutorials instead suggest making an #Inject field in
// your application and calling inject(application).
}

Tapestry: Inject at runtime

again a small problem by understanding "how tapestry works".
I've got a Tapestry component (in this case a value encoder):
public class EditionEncoder implements ValueEncoder<Edition>, ValueEncoderFactory<Edition> {
#Inject
private IEditionManager editionDao;
public EditionEncoder(IEditionManager editionDao) {
this.editionManager = editionDao;
}
#Override
public String toClient(Edition value) {
if(value == null) {
return "";
}
return value.getName();
}
#Override
public Edition toValue(String clientValue) {
if(clientValue.equals("")) {
return null;
}
return editionManager.getEditionByName(clientValue);
}
#Override
public ValueEncoder<Edition> create(Class<Edition> type) {
return this;
}
}
Injecting the the Manager is not working, because the Encoder is created within a page like that:
public void create() {
editionEncoder = new EditionEncoder();
}
casued by this, i'm forced to use this ugly solution:
#Inject
private IEditionManager editionmanager;
editionEncoder = new EditionEncoder(editionManager);
Is there a better way to inject components during runtime or is there a better solution in general for it?
Thanks for your help in advance,
As soon as you use "new" then tapestry-ioc is not involved in object creation and can't inject. You should inject everything and never use "new" for singleton services. This is true for all ioc containers, not just tapestry-ioc.
Also if you put #Inject on a field then you don't also need a constructor to set it. Do one or the other, never both.
You should do something like this:
public class MyAppModule {
public void bind(ServiceBinder binder) {
binder.bind(EditionEncoder.class);
}
}
Then in your page/component/service
#Inject EditionEncoder editionEncoder;
If you wanted to put your own instantiated objects in there you can do
public class MyServiceModule {
public void bind(ServiceBinder binder) {
binder.bind(Service1.class, Service1Impl.class);
binder.bind(Service2.class, Service2Impl.class);
}
public SomeService buildSomeService(Service1 service1, Service2 service2, #AutoBuild Service3Impl service3) {
Date someDate = new Date();
return new SomeServiceImpl(service1, service2, service3, someDate);
}
}

Is there a difference between the way Dagger2 treats #Singleton and custom sopes?

The question is in the subject, yet I'll repeat it again:
Is there a difference between the way Dagger2 treats #Singleton and custom sopes?
Also, if a class is annotated with some scope, is there a convenient way to expose it as a different scope (or unscoped), or do I need to write a provider method?
There is no difference between the way Dagger2 treats #Singleton and custom sopes.
Lets say we are using #User
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface User {
}
#Module
public class TwitterModule {
private final String user;
public TwitterModule(String user) {
this.user = user;
}
#Provides
#User
Tweeter provideTweeter(TwitterApi twitterApi) {
return new Tweeter(twitterApi, user);
}
#Provides
#User
Timeline provideTimeline(TwitterApi twitterApi) {
return new Timeline(twitterApi, user);
}
}
#Module
public class NetworkModule {
#Provides
#Singleton
OkHttpClient provideOkHttpClient() {
return new OkHttpClient();
}
#Provides
#Singleton
TwitterApi provideTwitterApi(OkHttpClient okHttpClient) {
return new TwitterApi(okHttpClient);
}
}
#Singleton
#Component(modules = {NetworkModule.class})
public interface ApiComponent {
TwitterApi api();
TwitterComponent twitterComponent(TwitterModule twitterModule);
}
#User
#Subcomponent(modules = {TwitterModule.class})
public interface TwitterComponent {
TwitterApplication app();
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
TwitterComponent twitterComponentForUserOne,twitterComponentForUserTwo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiComponent apiComponent = DaggerApiComponent.create();
twitterComponentForUserOne = apiComponent.twitterComponent(new TwitterModule("Amit Shekhar"));
twitterComponentForUserTwo = apiComponent.twitterComponent(new TwitterModule("Sumit Shekhar"));
// use twitterComponentOne and twitterComponentTwo for two users independently
}
#Override
protected void onDestroy() {
super.onDestroy();
twitterComponentForUserOne = null;
twitterComponentForUserTwo = null;
}
}
Here just we have to make sure that when we do not need the twitterComponent for that user. We have to assign null so that it gets garbage collected as I am doing here in onDestroy();
Finally, everything depends on component,if you have an instance of component in Application class it is not going to be garbage collected for whole application life-cycle.

Can I pass arguments into GWT Module constructor?

I have the following GWT module:
public class FizzModule implements EntryPoint {
private Buzz buzz;
public FizzModule() {
this(null);
}
public FizzModule(Buzz bz) {
super();
setBuzz(bz);
}
#Override
public void onModuleLoad() {
// ...etc.
}
}
I would like to "inject" FizzModule with a Buzz instance. However, all of the code examples I see for GWT modules do not use constructors. Instead, they bootstrap the DI mechanism (typically either ClientFactory or GIN) from inside the onModuleLoad() method. Is this something that GWT forces, or can I somehow inject my module before it loads to the client-side? Thanks in advance!
GWT instantiates your module using its zero-arg constructor, always.
(technically, I think it uses GWT.create() so you could use deferred binding rules, but that wouldn't change anything re. how its instantiated)
BTW, where would the Buzz instance come from?
You could add parameters to the URL and use PlaceController. Then get those values on module load.
public void onModuleLoad() {
SimplePanel mainPanel = new SimplePanel();
EventBus eventBus = GWT.creat(EventBus.class);
// Start ActivityManager for the main widget with ActivityMapper
ActivityManager activityManager = new ActivityManager(injector.getActivityMapper(),
eventBus);
activityManager.setDisplay(mainPanel);
RootPanel.get().add(mainPanel);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper contentHistoryMapper = GWT.create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(contentHistoryMapper);
PlaceController placeController = new PlaceController(eventBus)
historyHandler.register(placeController, injector.getEventBus(), new MainPlace());
// Goes to the place represented on URL else default place
historyHandler.handleCurrentHistory();
if(placeController.getWhere() instanceof MainPlace) {
(MainPlace).getFoo();
}
}
public class MainPlace extends Place {
private String foo;
public MainPlace(String token) {
String foo = token;
}
#Override
public String getFoo() {
return foo;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(token);
}
#Override
public String getToken(MainPlace place) {
return place.getFoo();
}
}
}

Categories