Conversation
|
Now everything (almost) is configurable via DI container and the things remain perfectly the same until now if no DI container is detected. Only one new tiny dependency is added ( From the beginning, we wanted this feature to be as unobtrusive as possible, with as less as possible changes. I think that we can do the code more compact but this is another story. For example for each inject aware field we have a declaration and a lazy initialization getter (and in some cases a setter): @Inject
private Optional<ErrorHandler> errorHandler = Optional.empty();
public ErrorHandler getErrorHandler() {
if (!errorHandler.isPresent()) {
errorHandler = Optional.of(new DefaultErrorHandler(this));
}
return errorHandler.get();
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = Optional.of(errorHandler);
}I prefer something more verbose/light as: @Inject
private Optional<ErrorHandler> errorHandler;
public ErrorHandler getErrorHandler() {
return OptionalUtils.setOnNull(Supplier<ErrorHandler>).get();
}Also the proposed implementation with lazy initialization getters come with some improvements from performance point of view because the objects are initialized only on request. That is all. I will add in my next two comments how I tested with Spring and Guice. If this PR will be accepted, after merge I will update |
|
For Spring Test, I modified pippo-demo-spring public class SpringApplication3 extends ControllerApplication {
@Inject
private List<? extends Controller> controllers;
@Override
protected void onInit() {
// add routes for static content
addPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(new Controller[0]));
}
}@Configuration
@ComponentScan
public class SpringConfiguration3 extends SpringConfiguration {
@Bean
public ContactService contactService() {
return new InMemoryContactService();
}
@Bean
public TemplateEngine templateEngine() {
return new SimpleTemplateEngine();
}
@Bean
public Router router() {
return new CustomRouter();
}
@Bean
public WebServer webServer() {
return new TjwsServer();
}
@Bean
public PippoSettings pippoSettings() {
return new PippoSettings();
}
@Bean
public Application application() {
return new SpringApplication3();
}
@Bean
public Pippo pippo() {
return new Pippo(application()).setServer(webServer());
}
}public class SpringDemo3 {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration3.class);
Pippo pippo = context.getBean(Pippo.class);
pippo.start();
}
}@Path
@Component
public class ContactsController extends Controller {
@Inject
private ContactService contactService;
@Inject
private TemplateEngine templateEngine;
@GET
public void sayHello() {
StringWriter writer = new StringWriter();
Map<String, Object> model = new HashMap<>();
model.put("name", "Decebal");
templateEngine.renderString("Hello ${name}", model, writer);
getResponse().send(writer.toString());
}
} |
|
For Guice Test, I modified pippo-demo-guice public class GuiceApplication3 extends ControllerApplication {
@Inject
private List<? extends Controller> controllers;
@Override
protected void onInit() {
// add routes for static content
addPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(new Controller[0]));
}
}public class GuiceModule3 extends AbstractModule {
@Override
protected void configure() {
bind(ContactService.class).to(InMemoryContactService.class).asEagerSingleton();
bind(Application.class).to(GuiceApplication3.class).asEagerSingleton();
bind(Router.class).to(CustomRouter.class).in(Scopes.SINGLETON);
bind(TemplateEngine.class).to(SimpleTemplateEngine.class).asEagerSingleton();
bind(WebServer.class).to(TjwsServer.class).in(Scopes.SINGLETON);
bind(Pippo.class);
bindOptionalApplication();
bindOptionalControllerApplication();
}
@Singleton
@Provides
@Inject
public List<? extends Controller> controllers(ContactsController contacts) {
return Arrays.asList(contacts);
}
private void bindOptionalApplication() {
OptionalBinder.newOptionalBinder(binder(), ContentTypeEngines.class);
OptionalBinder.newOptionalBinder(binder(), ErrorHandler.class);
OptionalBinder.newOptionalBinder(binder(), HttpCacheToolkit.class);
OptionalBinder.newOptionalBinder(binder(), Languages.class);
OptionalBinder.newOptionalBinder(binder(), Messages.class);
OptionalBinder.newOptionalBinder(binder(), MimeTypes.class);
OptionalBinder.newOptionalBinder(binder(), Router.class);
OptionalBinder.newOptionalBinder(binder(), WebSocketRouter.class);
OptionalBinder.newOptionalBinder(binder(), RequestResponseFactory.class);
OptionalBinder.newOptionalBinder(binder(), RoutePreDispatchListenerList.class);
OptionalBinder.newOptionalBinder(binder(), RoutePostDispatchListenerList.class);
OptionalBinder.newOptionalBinder(binder(), TemplateEngine.class);
OptionalBinder.newOptionalBinder(binder(), new TypeLiteral<RouteHandler<?>>(){});
OptionalBinder.newOptionalBinder(binder(), new TypeLiteral<List<Initializer>>(){});
}
private void bindOptionalControllerApplication() {
OptionalBinder.newOptionalBinder(binder(), ControllerFactory.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInitializationListenerList.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInstantiationListenerList.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInvokeListenerList.class);
OptionalBinder.newOptionalBinder(binder(), new TypeLiteral<List<MethodParameterExtractor>>(){});
}
}public class GuiceDemo3 {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new GuiceModule3());
Pippo pippo = injector.getInstance(Pippo.class);
pippo.start();
}
}@Path
public class ContactsController extends Controller {
@Inject
private ContactService contactService;
@Inject
private TemplateEngine templateEngine;
@GET
public void sayHello() {
StringWriter writer = new StringWriter();
Map<String, Object> model = new HashMap<>();
model.put("name", "Decebal");
templateEngine.renderString("Hello ${name}", model, writer);
getResponse().send(writer.toString());
}
}I don't like the complexity of |
Sorry I didn't understand why What if I have dozens of controllers? |
|
@decebals , will you still make changes or can I test this version in my application? |
I think that you can test it. The code is perfect functional from what I see until now. Maybe little adjustments in time. |
Your question is good. In my last (relative big) project I use Pippo with Spring. In Spring this part with gathering all controllers and inject them in application is easy and automatically. All you have to do is to add |
In the end I think that I solved the problem in an elegant way using Guice Multibinding and Reflections. public class GuiceModule3 extends AbstractModule {
@Override
protected void configure() {
bind(ContactService.class).to(InMemoryContactService.class).asEagerSingleton();
bind(Application.class).to(GuiceApplication3.class).asEagerSingleton();
bind(Router.class).to(CustomRouter.class).in(Scopes.SINGLETON);
bind(TemplateEngine.class).to(SimpleTemplateEngine.class).asEagerSingleton();
bind(WebServer.class).to(TjwsServer.class).in(Scopes.SINGLETON);
bind(Pippo.class);
bindControllers();
bindOptionalApplication();
bindOptionalControllerApplication();
}
private void bindControllers() {
// retrieve controller classes
Reflections reflections = new Reflections(getClass().getPackage().getName());
Set<Class<? extends Controller>> controllers = reflections.getSubTypesOf(Controller.class);
// bind found controllers
Multibinder<Controller> multibinder = Multibinder.newSetBinder(binder(), Controller.class);
controllers.forEach(controller -> multibinder.addBinding().to(controller));
}
}public class GuiceApplication3 extends ControllerApplication {
@Inject
private Set<Controller> controllers;
@Override
protected void onInit() {
// add routes for static content
addPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(new Controller[0]));
}
}I tested with multiple controllers and the result is good. |
|
I already use the Reflections lib and it is very good! |
My boot is heavily modified, so for now I won't be able to test it thoroughly. But I have good news: with this version my application continues to work normally. |
|
I'm trying to adapt my application to this model... @decebals , I use the The
|
For me, Guice's dependency injection just only worked like this: @Inject
private Set<Controller> controllers;obs: |
Yes, it's |
|
In #590 (comment), it's |
I inject As I mentioned in #565, the pippo - spring integration is good enough for me and without this PR. This PR is useful when you want to fine tuning the pippo stack from DI (Spring, Guice), entirely. |
|
I obtained relative ( The code in this case looks like: //@Singleton
public class AvajeApplication extends ControllerApplication {
private List<Controller> controllers;
// @Inject
public AvajeApplication(List<Controller> controllers) {
this.controllers = controllers;
}
@Override
protected void onInit() {
// add routes for static content
addPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(new Controller[0]));
}
}@Factory
public class AvajeConfiguration {
@Bean
public ContactService contactService() {
return new InMemoryContactService();
}
@Bean
public PippoSettings pippoSettings() {
return new PippoSettings();
}
// @Bean
// public List<Controller> controllers(ContactsController contactsController) {
// System.out.println("AvajeConfiguration.controllers");
// return Collections.singletonList(contactsController);
// }
@Bean
public Application application(ContactsController contactsController, TestController testController) {
return new AvajeApplication(Arrays.asList(contactsController, testController));
}
// @Bean
// public Pippo pippo(Application application, WebServer webServer) {
// return new Pippo(application).setServer(webServer);
// }
}public class AvajeDemo {
public static void main(String[] args) {
BeanScope beanScope = BeanScope.newBuilder().build();
Pippo pippo = beanScope.get(Pippo.class);
pippo.start();
}
}@Path
@Singleton
public class ContactsController extends Controller {
@Inject
ContactService contactService;
@Inject
TemplateEngine templateEngine;
@GET
public void index() {
getResponse().bind("contacts", contactService.getContacts());
getResponse().render("contacts");
}
} |
Hi! I understand that it is possible to inject To explain it better, something like this: Injector injector = Guice.createInjector(
new PippoGuiceModule(),
new AppJpaPersistModule("persistenceUnitName", pippoSettings), // <<< need PippoSettings instance here
new AppGuiceModule()
);
GuiceInjector.set(injector);
Pippo pippo = injector.getInstance(Pippo.class);
pippo.start();ps: I'm looking for a way to work around this problem. |
We might have abstract controllers, so maybe it's better to avoid bind errors (at least in Guice): Reflections reflections = new Reflections(getClass().getPackage().getName(), new SubTypesScanner());
Set<Class<? extends Controller>> controllers = reflections.getSubTypesOf(Controller.class)
.stream()
.filter(clazz -> clazz.isAnnotationPresent(ro.pippo.controller.Path.class))
.collect(Collectors.toSet())Or some other logic that checks if it's a concrete class. |
I don't visualize your implementation. How |
What about https://stackoverflow.com/questions/39734343/injecting-a-dependency-into-guice-module? |
Oh, sorry 😅, I forgot to mention it's a class of mine. It's just a wrapper for Guice's It goes something like this: public class AppJpaPersistModule implements Module {
private final PippoSettings settings;
public JPAGuiceModule(PippoSettings settings) {
this.settings = settings;
}
@Override
public void configure(Binder binder) {
JpaPersistModule jpaModule = new JpaPersistModule(Constantes.PU_NAME);
jpaModule.properties( ... ); // TODO: get properties from PippoSettings and add here
binder.install(jpaModule);
}
}ps: But I think I'll change the strategy so I don't need |
|
@decebals , I use Freemarker and it's not working. The problem is that the To make it work I did:
I configure the Guice module like this: @Override
protected void configure() {
bind(Application.class).to(PippoApplication.class).asEagerSingleton();
bind(TemplateEngine.class).to(FreemarkerTemplateEngine.class).asEagerSingleton();
// ...
}It would be nice to be able to leave the annotation just on the |
@mhagnumdw |
# Conflicts: # pippo-controller-parent/pippo-controller/src/main/java/ro/pippo/controller/ControllerApplication.java
|
@decebals , please update from master. |
# Conflicts: # pippo-controller-parent/pippo-controller/src/main/java/ro/pippo/controller/ControllerApplication.java
Done. |
|
@decebals , please update from master. |
Done |
|
Currently I register a content type like this: What do you think we also use dependency injection to register In Guice we can use If you agree, could you implement it? So I would validate doing the tests in my application. |
Sure, I will do it. Now I am in a mini vacation with family. |
|
Kudos, SonarCloud Quality Gate passed!
|
|
@mhagnumdw I don't know if you abandoned the ship but I will write here some of my conclusions :). import javax.annotation.Nullable;
import javax.inject.Inject;
class MyClass {
@Inject @Nullable
private Greeting greeting;
}Both Spring and Guice know how to deal with this combination of annotations. import javax.annotation.Nullable;
import javax.inject.Inject;
class MyClass {
private Greeting greeting;
@Inject
public void setGreeting(@Nullable Greeting greeting) {
this.greeting = greeting;
}
}What I don't like is that in all situations (our initial solution based on And this problem is because import org.springframework.beans.factory.annotation.Autowired;
import com.google.inject.Inject;
class MyClass {
@Autowired(required = false)
@Inject(optional = true)
private Greeting greeting;
}but in this case we must add Everything started from the idea to have something/everything configurable via most popular java IoC (Spring and Guice), but without forcing the pippo developer to use IoC (or a specific IoC). |
|
Hi @decebals !! I paused this activity for timing reasons. I still think it's a worthwhile activity. But unfortunately, given its magnitude, I won't have time to see it carefully. My application that uses Pippo is only receiving fixes and they are sporadic. |








#554