Trying to understand how to connect to MongoDB in Play Framework JAVA (version 2.4x) using built in DI framework, Guice.
So far, I'm using Jongo (version 1.2). I've create a JongoProvider class where I initialise MongoDB instance with relevant ip address to MongoDB, etc.
I inject this into a model like below..., however it seems I can't access the injected instance in a static method. Is there a correct way of accessing inject Mongo object?
class User {
#Inject JongoProvider jongo;
public static User getUsers() {
// can't access static instance
jongo.getcollection("users");
}
}
//=== Edit based on Mon Calamari's answer
class User {
public static JongoProvider jongo() {
return Play.application().injector().instanceOf(JongoProvider.class);
}
public static User getUsers() {
jongo().getcollection("users");
}
}
Class User does not belong to Guice dependency injection framework, thus you cannot inject Guice dependency into something Guice knows nothing about. Try using static methods on Play object:
JongoProvider provicer = Play.application().injector().instanceOf(JongoProvider.class)
Related
I have an spring boot app that has a mongo repository and is using spring data to connect to it. This means there's an "entity" class and then a "repository" class.
However, to actually use the repository I need spring to #Autowire my repo to a variable inside another class (we'll call it X) that might want to use it. But if the X class is itself not a spring bean, it's just a regular pojo created by "new X" somewhere then it can't make use of Autowiring and therefore cant use the repo.
This seems like a show stopper... No one could ever make use of a repo outside of some very specific situations like calling the repo directly from your RestController or whatever without any intervening logic. Yet I am sure people are using this.
So my question is how to structure code so that it can be used? Do I need to do a bunch of processing and then return back up to the controller to interact with the databases? Is there a way to create some kind of other "intermediate helper bean" to mediate the connection? What should that look like?
disclaimer: I am fairly new to spring
Perhaps I can post some excerpts that can clarify your situation.
//nothing spring specific
public class MyPojo {
//properties
}
in a different package:
#Repository
public class MyRepositoryImpl implements MyRepository {
//CRUD implementation or whatever
}
in a different package:
#Service
public class MyServiceImpl implements MyService {
#Autowired //constructor-injection
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
private final MyRepository myRepository;
public void myBusinessLogic() {
MyPojo pojo = new MyPojo(); //not dependent on Spring
myRepository.doSomething();
//place calls to X here as needed
}
}
And finally:
#Controller
public class MyController {
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
private final MyService myService;
#GetMapping("/myPage")
public String doIt() {
myService.myBusinessLogic();
return "myPage";
}
}
Where MyRepository and MyService are interfaces that would contain the contract for their respective implementations.
Single Responsibility Principle
A major point to note is that your POJO isn't going to "use the repo" as you mention in your question. It represents an entity and shouldn't care about any specific repository. And this seems related to your issue - a POJO shouldn't be making calls to a repository (your "X" class in this case). Seems like the design should be revisited if that is the case.
As you say, you can only autowire fields in objects that themselves are autowired. This is inherent to bean injection. You should annotate X with for instance #Component and inject it where you need it.
I have a class as below:
public class UserAuthenticator {
private static UserAuthenticator authenticator =
#Inject
private UserRepository userRepository;
#PostConstruct
public void init() {
List<User> allUsers = userRepository.findAll();
for (User user : allUsers) {
users.put(user.getEmail(), user.getPassword());
serviceKeys.put(user.getServiceKey(), user.getEmail());
}
}
public static UserAuthenticator getInstance() {
if (authenticator == null) {
authenticator = new UserAuthenticator();
}
return authenticator;
}
}
When I call
UserAuthenticator authenticator = UserAuthenticator.getInstance();
init() method isn't called and userRepository is null
My web application run in JBOSS EAP 6.3.
How is this caused and how can I solve it?
In a Java EE application, don't think in singletons. That's only recipe for trouble and confusion. Instead, think in "just create one". Tell the Java EE container to just create only one instance of the specified class, application wide, and obtain the instance via the facility offered by the Java EE container. Your concrete problem is caused because you're manually creating an instance of the class using new operator without manually performing the injection and post construct call like as the technical correct but conceptually wrong example below:
authenticator = new UserAuthenticator();
authenticator.userRepository = new UserRepository();
authenticator.init();
In other words, you incorrectly expected that the new operator magically recognizes the bean management and dependency injection related annotations.
The right approach depends on the one you'd like to point out as the responsible for creating and managing the instance of the specified class. If it's CDI, then just tell it to create only one managed bean instance of the backing bean class, application wide, using #Named #ApplicationScoped.
import javax.inject.Named;
import javax.enterprise.context.ApplicationScoped;
#Named
#ApplicationScoped
public class UserAuthenticator {}
It will be created just once and be available via #Inject as below in any other Java EE managed artifact (read: in any other class annotated with #Named, #Stateless, #ManagedBean, #WebServlet, #WebListener, #WebFilter, #Path, etc..):
#Inject
private UserAuthenticator userAuthenticator;
If you're absolutely positive that you need a static method to grab the current CDI managed bean instance of a given backing class, then you should be obtaining it via BeanManager as below instead of manually constructing the instance (assuming Java EE 7 / CDI 1.1 available):
#SuppressWarnings("unchecked")
public static <T> T getCurrentInstance(Class<T> beanClass) {
BeanManager beanManager = CDI.current().getBeanManager();
Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(beanClass));
return (T) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
}
Usage:
UserAuthenticator userAuthenticator = YourCDIUtil.getCurrentInstance(UserAuthenticator.class);
// ...
See also:
Java singleton class vs JSF application scoped managed bean - differences?
Java EE 6 and Singletons
Well i think you shouldn't explictly call UserAuthenticator.getInstance() but to define the UserAuthenticator for example as #ApplicationScoped and get the instance via DI provided by your app server (#Inject).
UserAuthenticator should be then initialized properly.
The #PostConstruct method will not be invoked until you do some action on that class (ex: call some methods
Let's say there are #Service and #Repository interfaces like the following:
#Repository
public interface OrderDao extends JpaRepository<Order, Integer> {
}
public interface OrderService {
void saveOrder(Order order);
}
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderDao orderDao;
#Override
#Transactional
public void saveOrder(Order order) {
orderDao.save(order);
}
}
This is part of working application, everything is configured to access single database and everything works fine.
Now, I would like to have possibility to create stand-alone working instance of OrderService with auto-wired OrderDao using pure Java with jdbcUrl specified in Java code, something like this:
final int tenantId = 3578;
final String jdbcUrl = "jdbc:mysql://localhost:3306/database_" + tenantId;
OrderService orderService = someMethodWithSpringMagic(appContext, jdbcUrl);
As you can see I would like to introduce multi-tenant architecture with tenant per database strategy to existing Spring-based application.
Please note that I was able to achieve that quite easily before with self-implemented jdbcTemplate-like logic also with JDBC transactions correctly working so this is very valid task.
Please also note that I need quite simple transaction logic to start transaction, do several requests in service method in scope of that transaction and then commit it/rollback on exception.
Most solutions on the web regarding multi-tenancy with Spring propose specifying concrete persistence units in xml config AND/OR using annotation-based configuration which is highly inflexible because in order to add new database url whole application should be stopped, xml config/annotation code should be changed and application started.
So, basically I'm looking for a piece of code which is able to create #Service just like Spring creates it internally after properties are read from XML configs / annotations. I'm also looking into using ProxyBeanFactory for that, because Spring uses AOP to create service instances (so I guess simple good-old re-usable OOP is not the way to go here).
Is Spring flexible enough to allow this relatively simple case of code reuse?
Any hints will be greatly appreciated and if I find complete answer to this question I'll post it here for future generations :)
HIbernate has out of the box support for multi tenancy, check that out before trying your own. Hibernate requires a MultiTenantConnectionProvider and CurrentTenantIdentifierResolver for which there are default implementations out of the box but you can always write your own implementation. If it is only a schema change it is actually pretty simple to implement (execute a query before returning the connection). Else hold a map of datasources and get an instance from that, or create a new instance.
About 8 years ago we already wrote a generic solution which was documented here and the code is here. It isn't specific for hibernate and could be used with basically anything you need to switch around. We used it for DataSources and also some web related things (theming amongst others).
Creating a transactional proxy for an annotated service is not a difficult task but I'm not sure that you really need it. To choose a database for a tenantId I guess that you only need to concentrate in DataSource interface.
For example, with a simple driver managed datasource:
public class MultitenancyDriverManagerDataSource extends DriverManagerDataSource {
#Override
protected Connection getConnectionFromDriverManager(String url,
Properties props) throws SQLException {
Integer tenant = MultitenancyContext.getTenantId();
if (tenant != null)
url += "_" + tenant;
return super.getConnectionFromDriverManager(url, props);
}
}
public class MultitenancyContext {
private static ThreadLocal<Integer> tenant = new ThreadLocal<Integer>();
public static Integer getTenantId() {
return tenant.get();
}
public static void setTenatId(Integer value) {
tenant.set(value);
}
}
Of course, If you want to use a connection pool, you need to elaborate it a bit, for example using a connection pool per tenant.
I've been going through the Play! 2.1 example to setup a basic login system following the ZenTasks example. Where I get stuck is the JavaForms part. I want to validate the login request using an instance of an auth service that is provided via Guice DI.
I'm following Play20 Sample. This example uses a static authenticate() method to run the authentication when form validation is requested after form submission. Any thoughts on how to perform this validation step in a non-static scope?
Note: I have looked at the Play! Authenticate plugin as well as the SecureSocial plugin, however those projects are overkill for what I want to do right now. Also, I am interested in a general solution for allowing non-static validation in JavaForms.
Edit:
It seems there is some confusion about what I am asking for here. What I am hoping to find is an alternate way to perform the validation step of the form submission that is sent by a Play! framework Form.form() generated form. Currently it requires that a validate() method be called on an instance of a POJO which is not created through the DI framework. This results in static references being required to access authorization services etc...
Edit 2:
The current solution I am working with is this:
public static class AuthServiceFormReference {
#Inject
public static Provider<AuthService> authService;
}
// In my auth module configure()
//...
requestStaticInjection(AuthController.AuthServiceFormReference.class);
//...
public static class Login {
#Required
public String email;
#Required
public String password;
public String validate(){
if(AuthServiceFormReference.authService.get().authenticateAdmin(email, password) == null) {
return "Invalid user or password";
}
return null;
}
}
It's an okay workaround, but it still relies on static injection :(
Play Framework does not offer Dependency Injection out of the box. However you can integrate it with Guice or Spring. As a lazy developer you could also create a Singleton for the service, or make it a plugin (as it probably needs to prepare work on application startup anyways). Then you can get a reference to your plugin -- Play.application().plugin(AuthPlugin.class).
In this particular case you can do a database lookup in the validate methods, e.g. User u = User.find.where().eq("username", username).eq("password`,password).findUnique();
if (u == null)
return "Error.";
else
return null;
So this isn't so much about Play Framework, but Java programming in general.
Assuming you use Spring, you can do it like in any other Java program:
#Configurable
public class MyModel {
#Autowired
transient MyService myService;
public String validate() { ... }
}
The #Configurable annotation makes the class capable of dependency injection. The transient qualifier makes sure the field doesn't get picked up as a field to save into a database.
Spring works fine with Play 2.0/2.1 in one of my projects. I don't consider dependency injection in model objects aesthetically pleasing, but if you need it you can use it.
I have a problem with injecting a bean into a helper class. It works basically like this: I create an object in the page constructor that does some work, returns some data and I show these on the page. In this helper object, a service should be injected via #Autowired annotation. However, I always get a null pointer exception when I use it. I also tried #SpringBean but it didn't help. On the other hand, when I inject this service directly into the page with #SpringBean, it's accessible and works fine. Do you know where the problem is?
This is the page:
public class Page extends BasePage {
public Page() {
HelperObject object = new HelperObject(new Application("APP_NAME"));
String result = object.getData();
add(new Label("label", result));
}
}
Helper object:
public class HelperObject {
private Application app;
#Autowired
private Service service;
public HelperObject(Application app) {
this.app = app;
}
public String getData() {
// use service, manipulate data, return a string
}
}
You can inject dependencies into non-Spring-non-Wicket-new-created objects using #SpringBean by calling InjectorHolder.getInjector().inject(this); in its constructor.
For example:
class MyPojo {
#SpringBean
MyDumbDAO dao;
MyPojo() {
InjectorHolder.getInjector().inject(this);
}
void justDoIt() {
dao.duh(); // dao is there!
}
}
Note that it will only work if called within a Wicket-managed request. If not (ie, if it's a Quartz job, or a Filter executed before Wicket's), the Application instance will not be available, and the injector won't know how to get the dependencies.
Another solution is to use Spring's #Configurable. It uses AspectJ to intercept creation of annotated objects, and inject its dependencies, even if you instantiate them directly with new (or some other framework, like Hibernate, creates them internally). But this requires runtime or build-time (works better for me) bytecode manipulation, which may be too much magic for some people.
#SpringBean only injects dependencies into classes that inherit from Wicket's Component. #Autowired only injects dependencies into classes created by Spring itself. That means you can't automatically inject a dependency into an object you create with new.
(Edit: you can also add a #SpringBean injection to your class by injecting in the constructor:
InjectorHolder.getInjector().inject(this);)
My normal workaround for this is to use my application class to help. (I'm a little puzzled by your use of new Application(...). I assume this isn't actually org.apache.wicket.Application.) For example:
public class MyApplication extends AuthenticatedWebApplication implements
ApplicationContextAware {
private ApplicationContext ctx;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.ctx = applicationContext;
}
public static MyApplication get() {
return (MyApplication) WebApplication.get();
}
public static Object getSpringBean(String bean) {
return get().ctx.getBean(bean);
}
public static <T> T getSpringBean(Class<T> bean) {
return get().ctx.getBean(bean);
}
....
}
In my Spring application context:
<!-- Set up wicket application -->
<bean id="wicketApplication" class="uk.co.humboldt.Project.MyApplication"/>
My helper object then looks up the service on demand:
public class HelperObject {
private Service getService() {
return MyApplication.getSpringBean(Service.class);
}
The best practice would be to create your objects via a factory bean (that has those properties injected by Spring, and have that factory inject those properties to objects it spawns - pure IoC).
You should really avoid using SpringContext all over the place (or any other similar solution for that matter).
Here is a partial list of reasons:
Your code gets coupled with Spring way too much (low-cohesion).
You mix plumbing code with the business-logic.
Your code is less readable.
It's less maintainable (e.g., changing the name of the service bean would lead to code modification - this violates SRP & OCP).
It's less testable (e.g., you need the Spring framework to test it).